QtApp  1.0
qcustomplot.cpp
Idź do dokumentacji tego pliku.
1 /***************************************************************************
2 ** **
3 ** QCustomPlot, an easy to use, modern plotting widget for Qt **
4 ** Copyright (C) 2011-2017 Emanuel Eichhammer **
5 ** **
6 ** This program is free software: you can redistribute it and/or modify **
7 ** it under the terms of the GNU General Public License as published by **
8 ** the Free Software Foundation, either version 3 of the License, or **
9 ** (at your option) any later version. **
10 ** **
11 ** This program is distributed in the hope that it will be useful, **
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of **
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
14 ** GNU General Public License for more details. **
15 ** **
16 ** You should have received a copy of the GNU General Public License **
17 ** along with this program. If not, see http://www.gnu.org/licenses/. **
18 ** **
19 ****************************************************************************
20 ** Author: Emanuel Eichhammer **
21 ** Website/Contact: http://www.qcustomplot.com/ **
22 ** Date: 04.09.17 **
23 ** Version: 2.0.0 **
24 ****************************************************************************/
25 
26 #include "qcustomplot.h"
27 
28 
29 /* including file 'src/vector2d.cpp', size 7340 */
30 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
31 
35 
43 /* start documentation of inline functions */
44 
106 /* end documentation of inline functions */
107 
112  mX(0),
113  mY(0)
114 {
115 }
116 
121 QCPVector2D::QCPVector2D(double x, double y) :
122  mX(x),
123  mY(y)
124 {
125 }
126 
131 QCPVector2D::QCPVector2D(const QPoint &point) :
132  mX(point.x()),
133  mY(point.y())
134 {
135 }
136 
141 QCPVector2D::QCPVector2D(const QPointF &point) :
142  mX(point.x()),
143  mY(point.y())
144 {
145 }
146 
153 {
154  double len = length();
155  mX /= len;
156  mY /= len;
157 }
158 
165 {
166  QCPVector2D result(mX, mY);
167  result.normalize();
168  return result;
169 }
170 
178 double QCPVector2D::distanceSquaredToLine(const QCPVector2D &start, const QCPVector2D &end) const
179 {
180  QCPVector2D v(end-start);
181  double vLengthSqr = v.lengthSquared();
182  if (!qFuzzyIsNull(vLengthSqr))
183  {
184  double mu = v.dot(*this-start)/vLengthSqr;
185  if (mu < 0)
186  return (*this-start).lengthSquared();
187  else if (mu > 1)
188  return (*this-end).lengthSquared();
189  else
190  return ((start + mu*v)-*this).lengthSquared();
191  } else
192  return (*this-start).lengthSquared();
193 }
194 
202 double QCPVector2D::distanceSquaredToLine(const QLineF &line) const
203 {
204  return distanceSquaredToLine(QCPVector2D(line.p1()), QCPVector2D(line.p2()));
205 }
206 
213 double QCPVector2D::distanceToStraightLine(const QCPVector2D &base, const QCPVector2D &direction) const
214 {
215  return qAbs((*this-base).dot(direction.perpendicular()))/direction.length();
216 }
217 
223 {
224  mX *= factor;
225  mY *= factor;
226  return *this;
227 }
228 
234 {
235  mX /= divisor;
236  mY /= divisor;
237  return *this;
238 }
239 
244 {
245  mX += vector.mX;
246  mY += vector.mY;
247  return *this;
248 }
249 
254 {
255  mX -= vector.mX;
256  mY -= vector.mY;
257  return *this;
258 }
259 /* end of 'src/vector2d.cpp' */
260 
261 
262 /* including file 'src/painter.cpp', size 8670 */
263 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
264 
268 
286  QPainter(),
287  mModes(pmDefault),
288  mIsAntialiasing(false)
289 {
290  // don't setRenderHint(QPainter::NonCosmeticDefautPen) here, because painter isn't active yet and
291  // a call to begin() will follow
292 }
293 
300 QCPPainter::QCPPainter(QPaintDevice *device) :
301  QPainter(device),
302  mModes(pmDefault),
303  mIsAntialiasing(false)
304 {
305 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
306  if (isActive())
307  setRenderHint(QPainter::NonCosmeticDefaultPen);
308 #endif
309 }
310 
317 void QCPPainter::setPen(const QPen &pen)
318 {
319  QPainter::setPen(pen);
320  if (mModes.testFlag(pmNonCosmetic))
321  makeNonCosmetic();
322 }
323 
331 void QCPPainter::setPen(const QColor &color)
332 {
333  QPainter::setPen(color);
334  if (mModes.testFlag(pmNonCosmetic))
335  makeNonCosmetic();
336 }
337 
345 void QCPPainter::setPen(Qt::PenStyle penStyle)
346 {
347  QPainter::setPen(penStyle);
348  if (mModes.testFlag(pmNonCosmetic))
349  makeNonCosmetic();
350 }
351 
360 void QCPPainter::drawLine(const QLineF &line)
361 {
362  if (mIsAntialiasing || mModes.testFlag(pmVectorized))
363  QPainter::drawLine(line);
364  else
365  QPainter::drawLine(line.toLine());
366 }
367 
374 void QCPPainter::setAntialiasing(bool enabled)
375 {
376  setRenderHint(QPainter::Antialiasing, enabled);
377  if (mIsAntialiasing != enabled)
378  {
379  mIsAntialiasing = enabled;
380  if (!mModes.testFlag(pmVectorized)) // antialiasing half-pixel shift only needed for rasterized outputs
381  {
382  if (mIsAntialiasing)
383  translate(0.5, 0.5);
384  else
385  translate(-0.5, -0.5);
386  }
387  }
388 }
389 
394 void QCPPainter::setModes(QCPPainter::PainterModes modes)
395 {
396  mModes = modes;
397 }
398 
410 bool QCPPainter::begin(QPaintDevice *device)
411 {
412  bool result = QPainter::begin(device);
413 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // before Qt5, default pens used to be cosmetic if NonCosmeticDefaultPen flag isn't set. So we set it to get consistency across Qt versions.
414  if (result)
415  setRenderHint(QPainter::NonCosmeticDefaultPen);
416 #endif
417  return result;
418 }
419 
426 {
427  if (!enabled && mModes.testFlag(mode))
428  mModes &= ~mode;
429  else if (enabled && !mModes.testFlag(mode))
430  mModes |= mode;
431 }
432 
442 {
444  QPainter::save();
445 }
446 
456 {
457  if (!mAntialiasingStack.isEmpty())
459  else
460  qDebug() << Q_FUNC_INFO << "Unbalanced save/restore";
461  QPainter::restore();
462 }
463 
469 {
470  if (qFuzzyIsNull(pen().widthF()))
471  {
472  QPen p = pen();
473  p.setWidth(1);
474  QPainter::setPen(p);
475  }
476 }
477 /* end of 'src/painter.cpp' */
478 
479 
480 /* including file 'src/paintbuffer.cpp', size 18502 */
481 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
482 
486 
507 /* start documentation of pure virtual functions */
508 
550 /* end documentation of pure virtual functions */
551 /* start documentation of inline functions */
552 
563 /* end documentation of inline functions */
564 
570 QCPAbstractPaintBuffer::QCPAbstractPaintBuffer(const QSize &size, double devicePixelRatio) :
571  mSize(size),
572  mDevicePixelRatio(devicePixelRatio),
573  mInvalidated(true)
574 {
575 }
576 
578 {
579 }
580 
590 {
591  if (mSize != size)
592  {
593  mSize = size;
595  }
596 }
597 
614 {
616 }
617 
628 {
629  if (!qFuzzyCompare(ratio, mDevicePixelRatio))
630  {
631 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
632  mDevicePixelRatio = ratio;
634 #else
635  qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4";
636  mDevicePixelRatio = 1.0;
637 #endif
638  }
639 }
640 
644 
657  QCPAbstractPaintBuffer(size, devicePixelRatio)
658 {
660 }
661 
663 {
664 }
665 
666 /* inherits documentation from base class */
668 {
669  QCPPainter *result = new QCPPainter(&mBuffer);
670  result->setRenderHint(QPainter::HighQualityAntialiasing);
671  return result;
672 }
673 
674 /* inherits documentation from base class */
676 {
677  if (painter && painter->isActive())
678  painter->drawPixmap(0, 0, mBuffer);
679  else
680  qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
681 }
682 
683 /* inherits documentation from base class */
684 void QCPPaintBufferPixmap::clear(const QColor &color)
685 {
686  mBuffer.fill(color);
687 }
688 
689 /* inherits documentation from base class */
691 {
692  setInvalidated();
693  if (!qFuzzyCompare(1.0, mDevicePixelRatio))
694  {
695 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
696  mBuffer = QPixmap(mSize*mDevicePixelRatio);
697  mBuffer.setDevicePixelRatio(mDevicePixelRatio);
698 #else
699  qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4";
700  mDevicePixelRatio = 1.0;
701  mBuffer = QPixmap(mSize);
702 #endif
703  } else
704  {
705  mBuffer = QPixmap(mSize);
706  }
707 }
708 
709 
710 #ifdef QCP_OPENGL_PBUFFER
711 
734 QCPPaintBufferGlPbuffer::QCPPaintBufferGlPbuffer(const QSize &size, double devicePixelRatio, int multisamples) :
736  mGlPBuffer(0),
737  mMultisamples(qMax(0, multisamples))
738 {
739  QCPPaintBufferGlPbuffer::reallocateBuffer();
740 }
741 
742 QCPPaintBufferGlPbuffer::~QCPPaintBufferGlPbuffer()
743 {
744  if (mGlPBuffer)
745  delete mGlPBuffer;
746 }
747 
748 /* inherits documentation from base class */
749 QCPPainter *QCPPaintBufferGlPbuffer::startPainting()
750 {
751  if (!mGlPBuffer->isValid())
752  {
753  qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
754  return 0;
755  }
756 
757  QCPPainter *result = new QCPPainter(mGlPBuffer);
758  result->setRenderHint(QPainter::HighQualityAntialiasing);
759  return result;
760 }
761 
762 /* inherits documentation from base class */
763 void QCPPaintBufferGlPbuffer::draw(QCPPainter *painter) const
764 {
765  if (!painter || !painter->isActive())
766  {
767  qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
768  return;
769  }
770  if (!mGlPBuffer->isValid())
771  {
772  qDebug() << Q_FUNC_INFO << "OpenGL pbuffer isn't valid, reallocateBuffer was not called?";
773  return;
774  }
775  painter->drawImage(0, 0, mGlPBuffer->toImage());
776 }
777 
778 /* inherits documentation from base class */
779 void QCPPaintBufferGlPbuffer::clear(const QColor &color)
780 {
781  if (mGlPBuffer->isValid())
782  {
783  mGlPBuffer->makeCurrent();
784  glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
785  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
786  mGlPBuffer->doneCurrent();
787  } else
788  qDebug() << Q_FUNC_INFO << "OpenGL pbuffer invalid or context not current";
789 }
790 
791 /* inherits documentation from base class */
792 void QCPPaintBufferGlPbuffer::reallocateBuffer()
793 {
794  if (mGlPBuffer)
795  delete mGlPBuffer;
796 
797  QGLFormat format;
798  format.setAlpha(true);
799  format.setSamples(mMultisamples);
800  mGlPBuffer = new QGLPixelBuffer(mSize, format);
801 }
802 #endif // QCP_OPENGL_PBUFFER
803 
804 
805 #ifdef QCP_OPENGL_FBO
806 
830 QCPPaintBufferGlFbo::QCPPaintBufferGlFbo(const QSize &size, double devicePixelRatio, QWeakPointer<QOpenGLContext> glContext, QWeakPointer<QOpenGLPaintDevice> glPaintDevice) :
832  mGlContext(glContext),
833  mGlPaintDevice(glPaintDevice),
834  mGlFrameBuffer(0)
835 {
836  QCPPaintBufferGlFbo::reallocateBuffer();
837 }
838 
839 QCPPaintBufferGlFbo::~QCPPaintBufferGlFbo()
840 {
841  if (mGlFrameBuffer)
842  delete mGlFrameBuffer;
843 }
844 
845 /* inherits documentation from base class */
846 QCPPainter *QCPPaintBufferGlFbo::startPainting()
847 {
848  if (mGlPaintDevice.isNull())
849  {
850  qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist";
851  return 0;
852  }
853  if (!mGlFrameBuffer)
854  {
855  qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
856  return 0;
857  }
858 
859  if (QOpenGLContext::currentContext() != mGlContext.data())
860  mGlContext.data()->makeCurrent(mGlContext.data()->surface());
861  mGlFrameBuffer->bind();
862  QCPPainter *result = new QCPPainter(mGlPaintDevice.data());
863  result->setRenderHint(QPainter::HighQualityAntialiasing);
864  return result;
865 }
866 
867 /* inherits documentation from base class */
868 void QCPPaintBufferGlFbo::donePainting()
869 {
870  if (mGlFrameBuffer && mGlFrameBuffer->isBound())
871  mGlFrameBuffer->release();
872  else
873  qDebug() << Q_FUNC_INFO << "Either OpenGL frame buffer not valid or was not bound";
874 }
875 
876 /* inherits documentation from base class */
877 void QCPPaintBufferGlFbo::draw(QCPPainter *painter) const
878 {
879  if (!painter || !painter->isActive())
880  {
881  qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
882  return;
883  }
884  if (!mGlFrameBuffer)
885  {
886  qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
887  return;
888  }
889  painter->drawImage(0, 0, mGlFrameBuffer->toImage());
890 }
891 
892 /* inherits documentation from base class */
893 void QCPPaintBufferGlFbo::clear(const QColor &color)
894 {
895  if (mGlContext.isNull())
896  {
897  qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
898  return;
899  }
900  if (!mGlFrameBuffer)
901  {
902  qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
903  return;
904  }
905 
906  if (QOpenGLContext::currentContext() != mGlContext.data())
907  mGlContext.data()->makeCurrent(mGlContext.data()->surface());
908  mGlFrameBuffer->bind();
909  glClearColor(color.redF(), color.greenF(), color.blueF(), color.alphaF());
910  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
911  mGlFrameBuffer->release();
912 }
913 
914 /* inherits documentation from base class */
915 void QCPPaintBufferGlFbo::reallocateBuffer()
916 {
917  // release and delete possibly existing framebuffer:
918  if (mGlFrameBuffer)
919  {
920  if (mGlFrameBuffer->isBound())
921  mGlFrameBuffer->release();
922  delete mGlFrameBuffer;
923  mGlFrameBuffer = 0;
924  }
925 
926  if (mGlContext.isNull())
927  {
928  qDebug() << Q_FUNC_INFO << "OpenGL context doesn't exist";
929  return;
930  }
931  if (mGlPaintDevice.isNull())
932  {
933  qDebug() << Q_FUNC_INFO << "OpenGL paint device doesn't exist";
934  return;
935  }
936 
937  // create new fbo with appropriate size:
938  mGlContext.data()->makeCurrent(mGlContext.data()->surface());
939  QOpenGLFramebufferObjectFormat frameBufferFormat;
940  frameBufferFormat.setSamples(mGlContext.data()->format().samples());
941  frameBufferFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
942  mGlFrameBuffer = new QOpenGLFramebufferObject(mSize*mDevicePixelRatio, frameBufferFormat);
943  if (mGlPaintDevice.data()->size() != mSize*mDevicePixelRatio)
944  mGlPaintDevice.data()->setSize(mSize*mDevicePixelRatio);
945 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
946  mGlPaintDevice.data()->setDevicePixelRatio(mDevicePixelRatio);
947 #endif
948 }
949 #endif // QCP_OPENGL_FBO
950 /* end of 'src/paintbuffer.cpp' */
951 
952 
953 /* including file 'src/layer.cpp', size 37064 */
954 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
955 
959 
1016 /* start documentation of inline functions */
1017 
1032 /* end documentation of inline functions */
1033 
1042 QCPLayer::QCPLayer(QCustomPlot *parentPlot, const QString &layerName) :
1043  QObject(parentPlot),
1044  mParentPlot(parentPlot),
1045  mName(layerName),
1046  mIndex(-1), // will be set to a proper value by the QCustomPlot layer creation function
1047  mVisible(true),
1048  mMode(lmLogical)
1049 {
1050  // Note: no need to make sure layerName is unique, because layer
1051  // management is done with QCustomPlot functions.
1052 }
1053 
1055 {
1056  // If child layerables are still on this layer, detach them, so they don't try to reach back to this
1057  // then invalid layer once they get deleted/moved themselves. This only happens when layers are deleted
1058  // directly, like in the QCustomPlot destructor. (The regular layer removal procedure for the user is to
1059  // call QCustomPlot::removeLayer, which moves all layerables off this layer before deleting it.)
1060 
1061  while (!mChildren.isEmpty())
1062  mChildren.last()->setLayer(0); // removes itself from mChildren via removeChild()
1063 
1064  if (mParentPlot->currentLayer() == this)
1065  qDebug() << Q_FUNC_INFO << "The parent plot's mCurrentLayer will be a dangling pointer. Should have been set to a valid layer or 0 beforehand.";
1066 }
1067 
1077 {
1078  mVisible = visible;
1079 }
1080 
1103 {
1104  if (mMode != mode)
1105  {
1106  mMode = mode;
1107  if (!mPaintBuffer.isNull())
1108  mPaintBuffer.data()->setInvalidated();
1109  }
1110 }
1111 
1119 {
1120  foreach (QCPLayerable *child, mChildren)
1121  {
1122  if (child->realVisibility())
1123  {
1124  painter->save();
1125  painter->setClipRect(child->clipRect().translated(0, -1));
1126  child->applyDefaultAntialiasingHint(painter);
1127  child->draw(painter);
1128  painter->restore();
1129  }
1130  }
1131 }
1132 
1142 {
1143  if (!mPaintBuffer.isNull())
1144  {
1145  if (QCPPainter *painter = mPaintBuffer.data()->startPainting())
1146  {
1147  if (painter->isActive())
1148  draw(painter);
1149  else
1150  qDebug() << Q_FUNC_INFO << "paint buffer returned inactive painter";
1151  delete painter;
1152  mPaintBuffer.data()->donePainting();
1153  } else
1154  qDebug() << Q_FUNC_INFO << "paint buffer returned zero painter";
1155  } else
1156  qDebug() << Q_FUNC_INFO << "no valid paint buffer associated with this layer";
1157 }
1158 
1173 {
1175  {
1176  if (!mPaintBuffer.isNull())
1177  {
1178  mPaintBuffer.data()->clear(Qt::transparent);
1180  mPaintBuffer.data()->setInvalidated(false);
1181  mParentPlot->update();
1182  } else
1183  qDebug() << Q_FUNC_INFO << "no valid paint buffer associated with this layer";
1184  } else if (mMode == lmLogical)
1185  mParentPlot->replot();
1186 }
1187 
1198 void QCPLayer::addChild(QCPLayerable *layerable, bool prepend)
1199 {
1200  if (!mChildren.contains(layerable))
1201  {
1202  if (prepend)
1203  mChildren.prepend(layerable);
1204  else
1205  mChildren.append(layerable);
1206  if (!mPaintBuffer.isNull())
1207  mPaintBuffer.data()->setInvalidated();
1208  } else
1209  qDebug() << Q_FUNC_INFO << "layerable is already child of this layer" << reinterpret_cast<quintptr>(layerable);
1210 }
1211 
1222 {
1223  if (mChildren.removeOne(layerable))
1224  {
1225  if (!mPaintBuffer.isNull())
1226  mPaintBuffer.data()->setInvalidated();
1227  } else
1228  qDebug() << Q_FUNC_INFO << "layerable is not child of this layer" << reinterpret_cast<quintptr>(layerable);
1229 }
1230 
1231 
1235 
1248 /* start documentation of inline functions */
1249 
1263 /* end documentation of inline functions */
1264 /* start documentation of pure virtual functions */
1265 
1306 /* end documentation of pure virtual functions */
1307 /* start documentation of signals */
1308 
1317 /* end documentation of signals */
1318 
1339 QCPLayerable::QCPLayerable(QCustomPlot *plot, QString targetLayer, QCPLayerable *parentLayerable) :
1340  QObject(plot),
1341  mVisible(true),
1342  mParentPlot(plot),
1343  mParentLayerable(parentLayerable),
1344  mLayer(0),
1345  mAntialiased(true)
1346 {
1347  if (mParentPlot)
1348  {
1349  if (targetLayer.isEmpty())
1351  else if (!setLayer(targetLayer))
1352  qDebug() << Q_FUNC_INFO << "setting QCPlayerable initial layer to" << targetLayer << "failed.";
1353  }
1354 }
1355 
1357 {
1358  if (mLayer)
1359  {
1360  mLayer->removeChild(this);
1361  mLayer = 0;
1362  }
1363 }
1364 
1371 {
1372  mVisible = on;
1373 }
1374 
1385 {
1386  return moveToLayer(layer, false);
1387 }
1388 
1394 bool QCPLayerable::setLayer(const QString &layerName)
1395 {
1396  if (!mParentPlot)
1397  {
1398  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1399  return false;
1400  }
1401  if (QCPLayer *layer = mParentPlot->layer(layerName))
1402  {
1403  return setLayer(layer);
1404  } else
1405  {
1406  qDebug() << Q_FUNC_INFO << "there is no layer with name" << layerName;
1407  return false;
1408  }
1409 }
1410 
1418 {
1419  mAntialiased = enabled;
1420 }
1421 
1433 {
1434  return mVisible && (!mLayer || mLayer->visible()) && (!mParentLayerable || mParentLayerable.data()->realVisibility());
1435 }
1436 
1471 double QCPLayerable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
1472 {
1473  Q_UNUSED(pos)
1474  Q_UNUSED(onlySelectable)
1475  Q_UNUSED(details)
1476  return -1.0;
1477 }
1478 
1497 {
1498  if (mParentPlot)
1499  {
1500  qDebug() << Q_FUNC_INFO << "called with mParentPlot already initialized";
1501  return;
1502  }
1503 
1504  if (!parentPlot)
1505  qDebug() << Q_FUNC_INFO << "called with parentPlot zero";
1506 
1509 }
1510 
1523 {
1525 }
1526 
1536 {
1537  if (layer && !mParentPlot)
1538  {
1539  qDebug() << Q_FUNC_INFO << "no parent QCustomPlot set";
1540  return false;
1541  }
1542  if (layer && layer->parentPlot() != mParentPlot)
1543  {
1544  qDebug() << Q_FUNC_INFO << "layer" << layer->name() << "is not in same QCustomPlot as this layerable";
1545  return false;
1546  }
1547 
1548  QCPLayer *oldLayer = mLayer;
1549  if (mLayer)
1550  mLayer->removeChild(this);
1551  mLayer = layer;
1552  if (mLayer)
1553  mLayer->addChild(this, prepend);
1554  if (mLayer != oldLayer)
1555  emit layerChanged(mLayer);
1556  return true;
1557 }
1558 
1566 void QCPLayerable::applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
1567 {
1568  if (mParentPlot && mParentPlot->notAntialiasedElements().testFlag(overrideElement))
1569  painter->setAntialiasing(false);
1570  else if (mParentPlot && mParentPlot->antialiasedElements().testFlag(overrideElement))
1571  painter->setAntialiasing(true);
1572  else
1573  painter->setAntialiasing(localAntialiased);
1574 }
1575 
1593 {
1594  Q_UNUSED(parentPlot)
1595 }
1596 
1609 {
1610  return QCP::iSelectOther;
1611 }
1612 
1623 {
1624  if (mParentPlot)
1625  return mParentPlot->viewport();
1626  else
1627  return QRect();
1628 }
1629 
1658 void QCPLayerable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
1659 {
1660  Q_UNUSED(event)
1661  Q_UNUSED(additive)
1662  Q_UNUSED(details)
1663  Q_UNUSED(selectionStateChanged)
1664 }
1665 
1678 void QCPLayerable::deselectEvent(bool *selectionStateChanged)
1679 {
1680  Q_UNUSED(selectionStateChanged)
1681 }
1682 
1708 void QCPLayerable::mousePressEvent(QMouseEvent *event, const QVariant &details)
1709 {
1710  Q_UNUSED(details)
1711  event->ignore();
1712 }
1713 
1726 void QCPLayerable::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
1727 {
1728  Q_UNUSED(startPos)
1729  event->ignore();
1730 }
1731 
1744 void QCPLayerable::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
1745 {
1746  Q_UNUSED(startPos)
1747  event->ignore();
1748 }
1749 
1776 void QCPLayerable::mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)
1777 {
1778  Q_UNUSED(details)
1779  event->ignore();
1780 }
1781 
1799 void QCPLayerable::wheelEvent(QWheelEvent *event)
1800 {
1801  event->ignore();
1802 }
1803 /* end of 'src/layer.cpp' */
1804 
1805 
1806 /* including file 'src/axis/range.cpp', size 12221 */
1807 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
1808 
1812 
1821 /* start of documentation of inline functions */
1822 
1864 /* end of documentation of inline functions */
1865 
1877 const double QCPRange::minRange = 1e-280;
1878 
1890 const double QCPRange::maxRange = 1e250;
1891 
1896  lower(0),
1897  upper(0)
1898 {
1899 }
1900 
1908 QCPRange::QCPRange(double lower, double upper) :
1909  lower(lower),
1910  upper(upper)
1911 {
1912  normalize();
1913 }
1914 
1927 void QCPRange::expand(const QCPRange &otherRange)
1928 {
1929  if (lower > otherRange.lower || qIsNaN(lower))
1930  lower = otherRange.lower;
1931  if (upper < otherRange.upper || qIsNaN(upper))
1932  upper = otherRange.upper;
1933 }
1934 
1947 void QCPRange::expand(double includeCoord)
1948 {
1949  if (lower > includeCoord || qIsNaN(lower))
1950  lower = includeCoord;
1951  if (upper < includeCoord || qIsNaN(upper))
1952  upper = includeCoord;
1953 }
1954 
1955 
1966 QCPRange QCPRange::expanded(const QCPRange &otherRange) const
1967 {
1968  QCPRange result = *this;
1969  result.expand(otherRange);
1970  return result;
1971 }
1972 
1983 QCPRange QCPRange::expanded(double includeCoord) const
1984 {
1985  QCPRange result = *this;
1986  result.expand(includeCoord);
1987  return result;
1988 }
1989 
1998 QCPRange QCPRange::bounded(double lowerBound, double upperBound) const
1999 {
2000  if (lowerBound > upperBound)
2001  qSwap(lowerBound, upperBound);
2002 
2003  QCPRange result(lower, upper);
2004  if (result.lower < lowerBound)
2005  {
2006  result.lower = lowerBound;
2007  result.upper = lowerBound + size();
2008  if (result.upper > upperBound || qFuzzyCompare(size(), upperBound-lowerBound))
2009  result.upper = upperBound;
2010  } else if (result.upper > upperBound)
2011  {
2012  result.upper = upperBound;
2013  result.lower = upperBound - size();
2014  if (result.lower < lowerBound || qFuzzyCompare(size(), upperBound-lowerBound))
2015  result.lower = lowerBound;
2016  }
2017 
2018  return result;
2019 }
2020 
2034 {
2035  double rangeFac = 1e-3;
2036  QCPRange sanitizedRange(lower, upper);
2037  sanitizedRange.normalize();
2038  // can't have range spanning negative and positive values in log plot, so change range to fix it
2039  //if (qFuzzyCompare(sanitizedRange.lower+1, 1) && !qFuzzyCompare(sanitizedRange.upper+1, 1))
2040  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0)
2041  {
2042  // case lower is 0
2043  if (rangeFac < sanitizedRange.upper*rangeFac)
2044  sanitizedRange.lower = rangeFac;
2045  else
2046  sanitizedRange.lower = sanitizedRange.upper*rangeFac;
2047  } //else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
2048  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0)
2049  {
2050  // case upper is 0
2051  if (-rangeFac > sanitizedRange.lower*rangeFac)
2052  sanitizedRange.upper = -rangeFac;
2053  else
2054  sanitizedRange.upper = sanitizedRange.lower*rangeFac;
2055  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0)
2056  {
2057  // find out whether negative or positive interval is wider to decide which sign domain will be chosen
2058  if (-sanitizedRange.lower > sanitizedRange.upper)
2059  {
2060  // negative is wider, do same as in case upper is 0
2061  if (-rangeFac > sanitizedRange.lower*rangeFac)
2062  sanitizedRange.upper = -rangeFac;
2063  else
2064  sanitizedRange.upper = sanitizedRange.lower*rangeFac;
2065  } else
2066  {
2067  // positive is wider, do same as in case lower is 0
2068  if (rangeFac < sanitizedRange.upper*rangeFac)
2069  sanitizedRange.lower = rangeFac;
2070  else
2071  sanitizedRange.lower = sanitizedRange.upper*rangeFac;
2072  }
2073  }
2074  // due to normalization, case lower>0 && upper<0 should never occur, because that implies upper<lower
2075  return sanitizedRange;
2076 }
2077 
2083 {
2084  QCPRange sanitizedRange(lower, upper);
2085  sanitizedRange.normalize();
2086  return sanitizedRange;
2087 }
2088 
2097 bool QCPRange::validRange(double lower, double upper)
2098 {
2099  return (lower > -maxRange &&
2100  upper < maxRange &&
2101  qAbs(lower-upper) > minRange &&
2102  qAbs(lower-upper) < maxRange &&
2103  !(lower > 0 && qIsInf(upper/lower)) &&
2104  !(upper < 0 && qIsInf(lower/upper)));
2105 }
2106 
2116 bool QCPRange::validRange(const QCPRange &range)
2117 {
2118  return (range.lower > -maxRange &&
2119  range.upper < maxRange &&
2120  qAbs(range.lower-range.upper) > minRange &&
2121  qAbs(range.lower-range.upper) < maxRange &&
2122  !(range.lower > 0 && qIsInf(range.upper/range.lower)) &&
2123  !(range.upper < 0 && qIsInf(range.lower/range.upper)));
2124 }
2125 /* end of 'src/axis/range.cpp' */
2126 
2127 
2128 /* including file 'src/selection.cpp', size 21906 */
2129 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
2130 
2134 
2160 /* start documentation of inline functions */
2161 
2220 /* end documentation of inline functions */
2221 
2226  mBegin(0),
2227  mEnd(0)
2228 {
2229 }
2230 
2237  mBegin(begin),
2238  mEnd(end)
2239 {
2240 }
2241 
2252 {
2253  QCPDataRange result(intersection(other));
2254  if (result.isEmpty()) // no intersection, preserve respective bounding side of otherRange as both begin and end of return value
2255  {
2256  if (mEnd <= other.mBegin)
2257  result = QCPDataRange(other.mBegin, other.mBegin);
2258  else
2259  result = QCPDataRange(other.mEnd, other.mEnd);
2260  }
2261  return result;
2262 }
2263 
2268 {
2269  return QCPDataRange(qMin(mBegin, other.mBegin), qMax(mEnd, other.mEnd));
2270 }
2271 
2283 {
2284  QCPDataRange result(qMax(mBegin, other.mBegin), qMin(mEnd, other.mEnd));
2285  if (result.isValid())
2286  return result;
2287  else
2288  return QCPDataRange();
2289 }
2290 
2296 bool QCPDataRange::intersects(const QCPDataRange &other) const
2297 {
2298  return !( (mBegin > other.mBegin && mBegin >= other.mEnd) ||
2299  (mEnd <= other.mBegin && mEnd < other.mEnd) );
2300 }
2301 
2307 bool QCPDataRange::contains(const QCPDataRange &other) const
2308 {
2309  return mBegin <= other.mBegin && mEnd >= other.mEnd;
2310 }
2311 
2312 
2313 
2317 
2351 /* start documentation of inline functions */
2352 
2378 /* end documentation of inline functions */
2379 
2384 {
2385 }
2386 
2391 {
2392  mDataRanges.append(range);
2393 }
2394 
2403 {
2404  if (mDataRanges.size() != other.mDataRanges.size())
2405  return false;
2406  for (int i=0; i<mDataRanges.size(); ++i)
2407  {
2408  if (mDataRanges.at(i) != other.mDataRanges.at(i))
2409  return false;
2410  }
2411  return true;
2412 }
2413 
2419 {
2420  mDataRanges << other.mDataRanges;
2421  simplify();
2422  return *this;
2423 }
2424 
2430 {
2431  addDataRange(other);
2432  return *this;
2433 }
2434 
2439 {
2440  for (int i=0; i<other.dataRangeCount(); ++i)
2441  *this -= other.dataRange(i);
2442 
2443  return *this;
2444 }
2445 
2450 {
2451  if (other.isEmpty() || isEmpty())
2452  return *this;
2453 
2454  simplify();
2455  int i=0;
2456  while (i < mDataRanges.size())
2457  {
2458  const int thisBegin = mDataRanges.at(i).begin();
2459  const int thisEnd = mDataRanges.at(i).end();
2460  if (thisBegin >= other.end())
2461  break; // since data ranges are sorted after the simplify() call, no ranges which contain other will come after this
2462 
2463  if (thisEnd > other.begin()) // ranges which don't fulfill this are entirely before other and can be ignored
2464  {
2465  if (thisBegin >= other.begin()) // range leading segment is encompassed
2466  {
2467  if (thisEnd <= other.end()) // range fully encompassed, remove completely
2468  {
2469  mDataRanges.removeAt(i);
2470  continue;
2471  } else // only leading segment is encompassed, trim accordingly
2472  mDataRanges[i].setBegin(other.end());
2473  } else // leading segment is not encompassed
2474  {
2475  if (thisEnd <= other.end()) // only trailing segment is encompassed, trim accordingly
2476  {
2477  mDataRanges[i].setEnd(other.begin());
2478  } else // other lies inside this range, so split range
2479  {
2480  mDataRanges[i].setEnd(other.begin());
2481  mDataRanges.insert(i+1, QCPDataRange(other.end(), thisEnd));
2482  break; // since data ranges are sorted (and don't overlap) after simplify() call, we're done here
2483  }
2484  }
2485  }
2486  ++i;
2487  }
2488 
2489  return *this;
2490 }
2491 
2497 {
2498  int result = 0;
2499  for (int i=0; i<mDataRanges.size(); ++i)
2500  result += mDataRanges.at(i).length();
2501  return result;
2502 }
2503 
2513 {
2514  if (index >= 0 && index < mDataRanges.size())
2515  {
2516  return mDataRanges.at(index);
2517  } else
2518  {
2519  qDebug() << Q_FUNC_INFO << "index out of range:" << index;
2520  return QCPDataRange();
2521  }
2522 }
2523 
2529 {
2530  if (isEmpty())
2531  return QCPDataRange();
2532  else
2533  return QCPDataRange(mDataRanges.first().begin(), mDataRanges.last().end());
2534 }
2535 
2542 void QCPDataSelection::addDataRange(const QCPDataRange &dataRange, bool simplify)
2543 {
2544  mDataRanges.append(dataRange);
2545  if (simplify)
2546  this->simplify();
2547 }
2548 
2555 {
2556  mDataRanges.clear();
2557 }
2558 
2569 {
2570  // remove any empty ranges:
2571  for (int i=mDataRanges.size()-1; i>=0; --i)
2572  {
2573  if (mDataRanges.at(i).isEmpty())
2574  mDataRanges.removeAt(i);
2575  }
2576  if (mDataRanges.isEmpty())
2577  return;
2578 
2579  // sort ranges by starting value, ascending:
2580  std::sort(mDataRanges.begin(), mDataRanges.end(), lessThanDataRangeBegin);
2581 
2582  // join overlapping/contiguous ranges:
2583  int i = 1;
2584  while (i < mDataRanges.size())
2585  {
2586  if (mDataRanges.at(i-1).end() >= mDataRanges.at(i).begin()) // range i overlaps/joins with i-1, so expand range i-1 appropriately and remove range i from list
2587  {
2588  mDataRanges[i-1].setEnd(qMax(mDataRanges.at(i-1).end(), mDataRanges.at(i).end()));
2589  mDataRanges.removeAt(i);
2590  } else
2591  ++i;
2592  }
2593 }
2594 
2606 {
2607  simplify();
2608  switch (type)
2609  {
2610  case QCP::stNone:
2611  {
2612  mDataRanges.clear();
2613  break;
2614  }
2615  case QCP::stWhole:
2616  {
2617  // whole selection isn't defined by data range, so don't change anything (is handled in plottable methods)
2618  break;
2619  }
2620  case QCP::stSingleData:
2621  {
2622  // reduce all data ranges to the single first data point:
2623  if (!mDataRanges.isEmpty())
2624  {
2625  if (mDataRanges.size() > 1)
2626  mDataRanges = QList<QCPDataRange>() << mDataRanges.first();
2627  if (mDataRanges.first().length() > 1)
2628  mDataRanges.first().setEnd(mDataRanges.first().begin()+1);
2629  }
2630  break;
2631  }
2632  case QCP::stDataRange:
2633  {
2634  mDataRanges = QList<QCPDataRange>() << span();
2635  break;
2636  }
2638  {
2639  // this is the selection type that allows all concievable combinations of ranges, so do nothing
2640  break;
2641  }
2642  }
2643 }
2644 
2652 {
2653  if (other.isEmpty()) return false;
2654 
2655  int otherIndex = 0;
2656  int thisIndex = 0;
2657  while (thisIndex < mDataRanges.size() && otherIndex < other.mDataRanges.size())
2658  {
2659  if (mDataRanges.at(thisIndex).contains(other.mDataRanges.at(otherIndex)))
2660  ++otherIndex;
2661  else
2662  ++thisIndex;
2663  }
2664  return thisIndex < mDataRanges.size(); // if thisIndex ran all the way to the end to find a containing range for the current otherIndex, other is not contained in this
2665 }
2666 
2676 {
2677  QCPDataSelection result;
2678  for (int i=0; i<mDataRanges.size(); ++i)
2679  result.addDataRange(mDataRanges.at(i).intersection(other), false);
2680  result.simplify();
2681  return result;
2682 }
2683 
2689 {
2690  QCPDataSelection result;
2691  for (int i=0; i<other.dataRangeCount(); ++i)
2692  result += intersection(other.dataRange(i));
2693  result.simplify();
2694  return result;
2695 }
2696 
2707 {
2708  if (isEmpty())
2709  return QCPDataSelection(outerRange);
2710  QCPDataRange fullRange = outerRange.expanded(span());
2711 
2712  QCPDataSelection result;
2713  // first unselected segment:
2714  if (mDataRanges.first().begin() != fullRange.begin())
2715  result.addDataRange(QCPDataRange(fullRange.begin(), mDataRanges.first().begin()), false);
2716  // intermediate unselected segments:
2717  for (int i=1; i<mDataRanges.size(); ++i)
2718  result.addDataRange(QCPDataRange(mDataRanges.at(i-1).end(), mDataRanges.at(i).begin()), false);
2719  // last unselected segment:
2720  if (mDataRanges.last().end() != fullRange.end())
2721  result.addDataRange(QCPDataRange(mDataRanges.last().end(), fullRange.end()), false);
2722  result.simplify();
2723  return result;
2724 }
2725 /* end of 'src/selection.cpp' */
2726 
2727 
2728 /* including file 'src/selectionrect.cpp', size 9224 */
2729 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
2730 
2734 
2757 /* start of documentation of inline functions */
2758 
2767 /* end of documentation of inline functions */
2768 /* start documentation of signals */
2769 
2806 /* end documentation of signals */
2807 
2814  QCPLayerable(parentPlot),
2815  mPen(QBrush(Qt::gray), 0, Qt::DashLine),
2816  mBrush(Qt::NoBrush),
2817  mActive(false)
2818 {
2819 }
2820 
2822 {
2823  cancel();
2824 }
2825 
2831 {
2832  if (axis)
2833  {
2834  if (axis->orientation() == Qt::Horizontal)
2835  return QCPRange(axis->pixelToCoord(mRect.left()), axis->pixelToCoord(mRect.left()+mRect.width()));
2836  else
2837  return QCPRange(axis->pixelToCoord(mRect.top()+mRect.height()), axis->pixelToCoord(mRect.top()));
2838  } else
2839  {
2840  qDebug() << Q_FUNC_INFO << "called with axis zero";
2841  return QCPRange();
2842  }
2843 }
2844 
2851 {
2852  mPen = pen;
2853 }
2854 
2862 {
2863  mBrush = brush;
2864 }
2865 
2871 {
2872  if (mActive)
2873  {
2874  mActive = false;
2875  emit canceled(mRect, 0);
2876  }
2877 }
2878 
2885 void QCPSelectionRect::startSelection(QMouseEvent *event)
2886 {
2887  mActive = true;
2888  mRect = QRect(event->pos(), event->pos());
2889  emit started(event);
2890 }
2891 
2898 void QCPSelectionRect::moveSelection(QMouseEvent *event)
2899 {
2900  mRect.setBottomRight(event->pos());
2901  emit changed(mRect, event);
2902  layer()->replot();
2903 }
2904 
2911 void QCPSelectionRect::endSelection(QMouseEvent *event)
2912 {
2913  mRect.setBottomRight(event->pos());
2914  mActive = false;
2915  emit accepted(mRect, event);
2916 }
2917 
2924 void QCPSelectionRect::keyPressEvent(QKeyEvent *event)
2925 {
2926  if (event->key() == Qt::Key_Escape && mActive)
2927  {
2928  mActive = false;
2929  emit canceled(mRect, event);
2930  }
2931 }
2932 
2933 /* inherits documentation from base class */
2935 {
2937 }
2938 
2946 {
2947  if (mActive)
2948  {
2949  painter->setPen(mPen);
2950  painter->setBrush(mBrush);
2951  painter->drawRect(mRect);
2952  }
2953 }
2954 /* end of 'src/selectionrect.cpp' */
2955 
2956 
2957 /* including file 'src/layout.cpp', size 79064 */
2958 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
2959 
2963 
2995 /* start documentation of inline functions */
2996 
3003 /* end documentation of inline functions */
3004 
3009  QObject(parentPlot),
3010  mParentPlot(parentPlot)
3011 {
3012  mChildren.insert(QCP::msLeft, QList<QCPLayoutElement*>());
3013  mChildren.insert(QCP::msRight, QList<QCPLayoutElement*>());
3014  mChildren.insert(QCP::msTop, QList<QCPLayoutElement*>());
3015  mChildren.insert(QCP::msBottom, QList<QCPLayoutElement*>());
3016 }
3017 
3019 {
3020  clear();
3021 }
3022 
3028 {
3029  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
3030  while (it.hasNext())
3031  {
3032  it.next();
3033  if (!it.value().isEmpty())
3034  return false;
3035  }
3036  return true;
3037 }
3038 
3044 {
3045  // make all children remove themselves from this margin group:
3046  QHashIterator<QCP::MarginSide, QList<QCPLayoutElement*> > it(mChildren);
3047  while (it.hasNext())
3048  {
3049  it.next();
3050  const QList<QCPLayoutElement*> elements = it.value();
3051  for (int i=elements.size()-1; i>=0; --i)
3052  elements.at(i)->setMarginGroup(it.key(), 0); // removes itself from mChildren via removeChild
3053  }
3054 }
3055 
3067 {
3068  // query all automatic margins of the layout elements in this margin group side and find maximum:
3069  int result = 0;
3070  const QList<QCPLayoutElement*> elements = mChildren.value(side);
3071  for (int i=0; i<elements.size(); ++i)
3072  {
3073  if (!elements.at(i)->autoMargins().testFlag(side))
3074  continue;
3075  int m = qMax(elements.at(i)->calculateAutoMargin(side), QCP::getMarginValue(elements.at(i)->minimumMargins(), side));
3076  if (m > result)
3077  result = m;
3078  }
3079  return result;
3080 }
3081 
3089 {
3090  if (!mChildren[side].contains(element))
3091  mChildren[side].append(element);
3092  else
3093  qDebug() << Q_FUNC_INFO << "element is already child of this margin group side" << reinterpret_cast<quintptr>(element);
3094 }
3095 
3103 {
3104  if (!mChildren[side].removeOne(element))
3105  qDebug() << Q_FUNC_INFO << "element is not child of this margin group side" << reinterpret_cast<quintptr>(element);
3106 }
3107 
3108 
3112 
3139 /* start documentation of inline functions */
3140 
3170 /* end documentation of inline functions */
3171 
3176  QCPLayerable(parentPlot), // parenthood is changed as soon as layout element gets inserted into a layout (except for top level layout)
3177  mParentLayout(0),
3178  mMinimumSize(),
3179  mMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX),
3180  mSizeConstraintRect(scrInnerRect),
3181  mRect(0, 0, 0, 0),
3182  mOuterRect(0, 0, 0, 0),
3183  mMargins(0, 0, 0, 0),
3184  mMinimumMargins(0, 0, 0, 0),
3185  mAutoMargins(QCP::msAll)
3186 {
3187 }
3188 
3190 {
3191  setMarginGroup(QCP::msAll, 0); // unregister at margin groups, if there are any
3192  // unregister at layout:
3193  if (qobject_cast<QCPLayout*>(mParentLayout)) // the qobject_cast is just a safeguard in case the layout forgets to call clear() in its dtor and this dtor is called by QObject dtor
3194  mParentLayout->take(this);
3195 }
3196 
3209 {
3210  if (mOuterRect != rect)
3211  {
3212  mOuterRect = rect;
3213  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
3214  }
3215 }
3216 
3229 {
3230  if (mMargins != margins)
3231  {
3232  mMargins = margins;
3233  mRect = mOuterRect.adjusted(mMargins.left(), mMargins.top(), -mMargins.right(), -mMargins.bottom());
3234  }
3235 }
3236 
3247 {
3248  if (mMinimumMargins != margins)
3249  {
3251  }
3252 }
3253 
3264 void QCPLayoutElement::setAutoMargins(QCP::MarginSides sides)
3265 {
3266  mAutoMargins = sides;
3267 }
3268 
3281 void QCPLayoutElement::setMinimumSize(const QSize &size)
3282 {
3283  if (mMinimumSize != size)
3284  {
3285  mMinimumSize = size;
3286  if (mParentLayout)
3288  }
3289 }
3290 
3298 void QCPLayoutElement::setMinimumSize(int width, int height)
3299 {
3300  setMinimumSize(QSize(width, height));
3301 }
3302 
3310 void QCPLayoutElement::setMaximumSize(const QSize &size)
3311 {
3312  if (mMaximumSize != size)
3313  {
3314  mMaximumSize = size;
3315  if (mParentLayout)
3317  }
3318 }
3319 
3327 void QCPLayoutElement::setMaximumSize(int width, int height)
3328 {
3329  setMaximumSize(QSize(width, height));
3330 }
3331 
3342 {
3343  if (mSizeConstraintRect != constraintRect)
3344  {
3345  mSizeConstraintRect = constraintRect;
3346  if (mParentLayout)
3348  }
3349 }
3350 
3364 void QCPLayoutElement::setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
3365 {
3366  QVector<QCP::MarginSide> sideVector;
3367  if (sides.testFlag(QCP::msLeft)) sideVector.append(QCP::msLeft);
3368  if (sides.testFlag(QCP::msRight)) sideVector.append(QCP::msRight);
3369  if (sides.testFlag(QCP::msTop)) sideVector.append(QCP::msTop);
3370  if (sides.testFlag(QCP::msBottom)) sideVector.append(QCP::msBottom);
3371 
3372  for (int i=0; i<sideVector.size(); ++i)
3373  {
3374  QCP::MarginSide side = sideVector.at(i);
3375  if (marginGroup(side) != group)
3376  {
3377  QCPMarginGroup *oldGroup = marginGroup(side);
3378  if (oldGroup) // unregister at old group
3379  oldGroup->removeChild(side, this);
3380 
3381  if (!group) // if setting to 0, remove hash entry. Else set hash entry to new group and register there
3382  {
3383  mMarginGroups.remove(side);
3384  } else // setting to a new group
3385  {
3386  mMarginGroups[side] = group;
3387  group->addChild(side, this);
3388  }
3389  }
3390  }
3391 }
3392 
3406 {
3407  if (phase == upMargins)
3408  {
3409  if (mAutoMargins != QCP::msNone)
3410  {
3411  // set the margins of this layout element according to automatic margin calculation, either directly or via a margin group:
3412  QMargins newMargins = mMargins;
3413  QList<QCP::MarginSide> allMarginSides = QList<QCP::MarginSide>() << QCP::msLeft << QCP::msRight << QCP::msTop << QCP::msBottom;
3414  foreach (QCP::MarginSide side, allMarginSides)
3415  {
3416  if (mAutoMargins.testFlag(side)) // this side's margin shall be calculated automatically
3417  {
3418  if (mMarginGroups.contains(side))
3419  QCP::setMarginValue(newMargins, side, mMarginGroups[side]->commonMargin(side)); // this side is part of a margin group, so get the margin value from that group
3420  else
3421  QCP::setMarginValue(newMargins, side, calculateAutoMargin(side)); // this side is not part of a group, so calculate the value directly
3422  // apply minimum margin restrictions:
3423  if (QCP::getMarginValue(newMargins, side) < QCP::getMarginValue(mMinimumMargins, side))
3424  QCP::setMarginValue(newMargins, side, QCP::getMarginValue(mMinimumMargins, side));
3425  }
3426  }
3427  setMargins(newMargins);
3428  }
3429  }
3430 }
3431 
3447 {
3448  return QSize(mMargins.left()+mMargins.right(), mMargins.top()+mMargins.bottom());
3449 }
3450 
3466 {
3467  return QSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
3468 }
3469 
3477 QList<QCPLayoutElement*> QCPLayoutElement::elements(bool recursive) const
3478 {
3479  Q_UNUSED(recursive)
3480  return QList<QCPLayoutElement*>();
3481 }
3482 
3494 double QCPLayoutElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
3495 {
3496  Q_UNUSED(details)
3497 
3498  if (onlySelectable)
3499  return -1;
3500 
3501  if (QRectF(mOuterRect).contains(pos))
3502  {
3503  if (mParentPlot)
3504  return mParentPlot->selectionTolerance()*0.99;
3505  else
3506  {
3507  qDebug() << Q_FUNC_INFO << "parent plot not defined";
3508  return -1;
3509  }
3510  } else
3511  return -1;
3512 }
3513 
3520 {
3521  foreach (QCPLayoutElement* el, elements(false))
3522  {
3523  if (!el->parentPlot())
3524  el->initializeParentPlot(parentPlot);
3525  }
3526 }
3527 
3538 {
3540 }
3541 
3554 {
3555 }
3556 
3560 
3584 /* start documentation of pure virtual functions */
3585 
3628 /* end documentation of pure virtual functions */
3629 
3635 {
3636 }
3637 
3648 {
3649  QCPLayoutElement::update(phase);
3650 
3651  // set child element rects according to layout:
3652  if (phase == upLayout)
3653  updateLayout();
3654 
3655  // propagate update call to child elements:
3656  const int elCount = elementCount();
3657  for (int i=0; i<elCount; ++i)
3658  {
3659  if (QCPLayoutElement *el = elementAt(i))
3660  el->update(phase);
3661  }
3662 }
3663 
3664 /* inherits documentation from base class */
3665 QList<QCPLayoutElement*> QCPLayout::elements(bool recursive) const
3666 {
3667  const int c = elementCount();
3668  QList<QCPLayoutElement*> result;
3669 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
3670  result.reserve(c);
3671 #endif
3672  for (int i=0; i<c; ++i)
3673  result.append(elementAt(i));
3674  if (recursive)
3675  {
3676  for (int i=0; i<c; ++i)
3677  {
3678  if (result.at(i))
3679  result << result.at(i)->elements(recursive);
3680  }
3681  }
3682  return result;
3683 }
3684 
3693 {
3694 }
3695 
3707 bool QCPLayout::removeAt(int index)
3708 {
3709  if (QCPLayoutElement *el = takeAt(index))
3710  {
3711  delete el;
3712  return true;
3713  } else
3714  return false;
3715 }
3716 
3729 {
3730  if (take(element))
3731  {
3732  delete element;
3733  return true;
3734  } else
3735  return false;
3736 }
3737 
3745 {
3746  for (int i=elementCount()-1; i>=0; --i)
3747  {
3748  if (elementAt(i))
3749  removeAt(i);
3750  }
3751  simplify();
3752 }
3753 
3763 {
3764  if (QWidget *w = qobject_cast<QWidget*>(parent()))
3765  w->updateGeometry();
3766  else if (QCPLayout *l = qobject_cast<QCPLayout*>(parent()))
3767  l->sizeConstraintsChanged();
3768 }
3769 
3783 {
3784 }
3785 
3786 
3800 {
3801  if (el)
3802  {
3803  el->mParentLayout = this;
3804  el->setParentLayerable(this);
3805  el->setParent(this);
3806  if (!el->parentPlot())
3808  el->layoutChanged();
3809  } else
3810  qDebug() << Q_FUNC_INFO << "Null element passed";
3811 }
3812 
3824 {
3825  if (el)
3826  {
3827  el->mParentLayout = 0;
3828  el->setParentLayerable(0);
3829  el->setParent(mParentPlot);
3830  // Note: Don't initializeParentPlot(0) here, because layout element will stay in same parent plot
3831  } else
3832  qDebug() << Q_FUNC_INFO << "Null element passed";
3833 }
3834 
3864 QVector<int> QCPLayout::getSectionSizes(QVector<int> maxSizes, QVector<int> minSizes, QVector<double> stretchFactors, int totalSize) const
3865 {
3866  if (maxSizes.size() != minSizes.size() || minSizes.size() != stretchFactors.size())
3867  {
3868  qDebug() << Q_FUNC_INFO << "Passed vector sizes aren't equal:" << maxSizes << minSizes << stretchFactors;
3869  return QVector<int>();
3870  }
3871  if (stretchFactors.isEmpty())
3872  return QVector<int>();
3873  int sectionCount = stretchFactors.size();
3874  QVector<double> sectionSizes(sectionCount);
3875  // if provided total size is forced smaller than total minimum size, ignore minimum sizes (squeeze sections):
3876  int minSizeSum = 0;
3877  for (int i=0; i<sectionCount; ++i)
3878  minSizeSum += minSizes.at(i);
3879  if (totalSize < minSizeSum)
3880  {
3881  // new stretch factors are minimum sizes and minimum sizes are set to zero:
3882  for (int i=0; i<sectionCount; ++i)
3883  {
3884  stretchFactors[i] = minSizes.at(i);
3885  minSizes[i] = 0;
3886  }
3887  }
3888 
3889  QList<int> minimumLockedSections;
3890  QList<int> unfinishedSections;
3891  for (int i=0; i<sectionCount; ++i)
3892  unfinishedSections.append(i);
3893  double freeSize = totalSize;
3894 
3895  int outerIterations = 0;
3896  while (!unfinishedSections.isEmpty() && outerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
3897  {
3898  ++outerIterations;
3899  int innerIterations = 0;
3900  while (!unfinishedSections.isEmpty() && innerIterations < sectionCount*2) // the iteration check ist just a failsafe in case something really strange happens
3901  {
3902  ++innerIterations;
3903  // find section that hits its maximum next:
3904  int nextId = -1;
3905  double nextMax = 1e12;
3906  for (int i=0; i<unfinishedSections.size(); ++i)
3907  {
3908  int secId = unfinishedSections.at(i);
3909  double hitsMaxAt = (maxSizes.at(secId)-sectionSizes.at(secId))/stretchFactors.at(secId);
3910  if (hitsMaxAt < nextMax)
3911  {
3912  nextMax = hitsMaxAt;
3913  nextId = secId;
3914  }
3915  }
3916  // check if that maximum is actually within the bounds of the total size (i.e. can we stretch all remaining sections so far that the found section
3917  // actually hits its maximum, without exceeding the total size when we add up all sections)
3918  double stretchFactorSum = 0;
3919  for (int i=0; i<unfinishedSections.size(); ++i)
3920  stretchFactorSum += stretchFactors.at(unfinishedSections.at(i));
3921  double nextMaxLimit = freeSize/stretchFactorSum;
3922  if (nextMax < nextMaxLimit) // next maximum is actually hit, move forward to that point and fix the size of that section
3923  {
3924  for (int i=0; i<unfinishedSections.size(); ++i)
3925  {
3926  sectionSizes[unfinishedSections.at(i)] += nextMax*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
3927  freeSize -= nextMax*stretchFactors.at(unfinishedSections.at(i));
3928  }
3929  unfinishedSections.removeOne(nextId); // exclude the section that is now at maximum from further changes
3930  } else // next maximum isn't hit, just distribute rest of free space on remaining sections
3931  {
3932  for (int i=0; i<unfinishedSections.size(); ++i)
3933  sectionSizes[unfinishedSections.at(i)] += nextMaxLimit*stretchFactors.at(unfinishedSections.at(i)); // increment all sections
3934  unfinishedSections.clear();
3935  }
3936  }
3937  if (innerIterations == sectionCount*2)
3938  qDebug() << Q_FUNC_INFO << "Exceeded maximum expected inner iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
3939 
3940  // now check whether the resulting section sizes violate minimum restrictions:
3941  bool foundMinimumViolation = false;
3942  for (int i=0; i<sectionSizes.size(); ++i)
3943  {
3944  if (minimumLockedSections.contains(i))
3945  continue;
3946  if (sectionSizes.at(i) < minSizes.at(i)) // section violates minimum
3947  {
3948  sectionSizes[i] = minSizes.at(i); // set it to minimum
3949  foundMinimumViolation = true; // make sure we repeat the whole optimization process
3950  minimumLockedSections.append(i);
3951  }
3952  }
3953  if (foundMinimumViolation)
3954  {
3955  freeSize = totalSize;
3956  for (int i=0; i<sectionCount; ++i)
3957  {
3958  if (!minimumLockedSections.contains(i)) // only put sections that haven't hit their minimum back into the pool
3959  unfinishedSections.append(i);
3960  else
3961  freeSize -= sectionSizes.at(i); // remove size of minimum locked sections from available space in next round
3962  }
3963  // reset all section sizes to zero that are in unfinished sections (all others have been set to their minimum):
3964  for (int i=0; i<unfinishedSections.size(); ++i)
3965  sectionSizes[unfinishedSections.at(i)] = 0;
3966  }
3967  }
3968  if (outerIterations == sectionCount*2)
3969  qDebug() << Q_FUNC_INFO << "Exceeded maximum expected outer iteration count, layouting aborted. Input was:" << maxSizes << minSizes << stretchFactors << totalSize;
3970 
3971  QVector<int> result(sectionCount);
3972  for (int i=0; i<sectionCount; ++i)
3973  result[i] = qRound(sectionSizes.at(i));
3974  return result;
3975 }
3976 
3990 {
3991  QSize minOuterHint = el->minimumOuterSizeHint();
3992  QSize minOuter = el->minimumSize(); // depending on sizeConstraitRect this might be with respect to inner rect, so possibly add margins in next four lines (preserving unset minimum of 0)
3993  if (minOuter.width() > 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
3994  minOuter.rwidth() += el->margins().left() + el->margins().right();
3995  if (minOuter.height() > 0 && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
3996  minOuter.rheight() += el->margins().top() + el->margins().bottom();
3997 
3998  return QSize(minOuter.width() > 0 ? minOuter.width() : minOuterHint.width(),
3999  minOuter.height() > 0 ? minOuter.height() : minOuterHint.height());;
4000 }
4001 
4015 {
4016  QSize maxOuterHint = el->maximumOuterSizeHint();
4017  QSize maxOuter = el->maximumSize(); // depending on sizeConstraitRect this might be with respect to inner rect, so possibly add margins in next four lines (preserving unset maximum of QWIDGETSIZE_MAX)
4018  if (maxOuter.width() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
4019  maxOuter.rwidth() += el->margins().left() + el->margins().right();
4020  if (maxOuter.height() < QWIDGETSIZE_MAX && el->sizeConstraintRect() == QCPLayoutElement::scrInnerRect)
4021  maxOuter.rheight() += el->margins().top() + el->margins().bottom();
4022 
4023  return QSize(maxOuter.width() < QWIDGETSIZE_MAX ? maxOuter.width() : maxOuterHint.width(),
4024  maxOuter.height() < QWIDGETSIZE_MAX ? maxOuter.height() : maxOuterHint.height());
4025 }
4026 
4027 
4031 
4052 /* start documentation of inline functions */
4053 
4068 /* end documentation of inline functions */
4069 
4074  mColumnSpacing(5),
4075  mRowSpacing(5),
4076  mWrap(0),
4077  mFillOrder(foRowsFirst)
4078 {
4079 }
4080 
4082 {
4083  // clear all child layout elements. This is important because only the specific layouts know how
4084  // to handle removing elements (clear calls virtual removeAt method to do that).
4085  clear();
4086 }
4087 
4096 QCPLayoutElement *QCPLayoutGrid::element(int row, int column) const
4097 {
4098  if (row >= 0 && row < mElements.size())
4099  {
4100  if (column >= 0 && column < mElements.first().size())
4101  {
4102  if (QCPLayoutElement *result = mElements.at(row).at(column))
4103  return result;
4104  else
4105  qDebug() << Q_FUNC_INFO << "Requested cell is empty. Row:" << row << "Column:" << column;
4106  } else
4107  qDebug() << Q_FUNC_INFO << "Invalid column. Row:" << row << "Column:" << column;
4108  } else
4109  qDebug() << Q_FUNC_INFO << "Invalid row. Row:" << row << "Column:" << column;
4110  return 0;
4111 }
4112 
4113 
4129 {
4130  if (!hasElement(row, column))
4131  {
4132  if (element && element->layout()) // remove from old layout first
4133  element->layout()->take(element);
4134  expandTo(row+1, column+1);
4135  mElements[row][column] = element;
4136  if (element)
4137  adoptElement(element);
4138  return true;
4139  } else
4140  qDebug() << Q_FUNC_INFO << "There is already an element in the specified row/column:" << row << column;
4141  return false;
4142 }
4143 
4155 {
4156  int rowIndex = 0;
4157  int colIndex = 0;
4158  if (mFillOrder == foColumnsFirst)
4159  {
4160  while (hasElement(rowIndex, colIndex))
4161  {
4162  ++colIndex;
4163  if (colIndex >= mWrap && mWrap > 0)
4164  {
4165  colIndex = 0;
4166  ++rowIndex;
4167  }
4168  }
4169  } else
4170  {
4171  while (hasElement(rowIndex, colIndex))
4172  {
4173  ++rowIndex;
4174  if (rowIndex >= mWrap && mWrap > 0)
4175  {
4176  rowIndex = 0;
4177  ++colIndex;
4178  }
4179  }
4180  }
4181  return addElement(rowIndex, colIndex, element);
4182 }
4183 
4190 bool QCPLayoutGrid::hasElement(int row, int column)
4191 {
4192  if (row >= 0 && row < rowCount() && column >= 0 && column < columnCount())
4193  return mElements.at(row).at(column);
4194  else
4195  return false;
4196 }
4197 
4210 void QCPLayoutGrid::setColumnStretchFactor(int column, double factor)
4211 {
4212  if (column >= 0 && column < columnCount())
4213  {
4214  if (factor > 0)
4215  mColumnStretchFactors[column] = factor;
4216  else
4217  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
4218  } else
4219  qDebug() << Q_FUNC_INFO << "Invalid column:" << column;
4220 }
4221 
4234 void QCPLayoutGrid::setColumnStretchFactors(const QList<double> &factors)
4235 {
4236  if (factors.size() == mColumnStretchFactors.size())
4237  {
4238  mColumnStretchFactors = factors;
4239  for (int i=0; i<mColumnStretchFactors.size(); ++i)
4240  {
4241  if (mColumnStretchFactors.at(i) <= 0)
4242  {
4243  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mColumnStretchFactors.at(i);
4244  mColumnStretchFactors[i] = 1;
4245  }
4246  }
4247  } else
4248  qDebug() << Q_FUNC_INFO << "Column count not equal to passed stretch factor count:" << factors;
4249 }
4250 
4263 void QCPLayoutGrid::setRowStretchFactor(int row, double factor)
4264 {
4265  if (row >= 0 && row < rowCount())
4266  {
4267  if (factor > 0)
4268  mRowStretchFactors[row] = factor;
4269  else
4270  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << factor;
4271  } else
4272  qDebug() << Q_FUNC_INFO << "Invalid row:" << row;
4273 }
4274 
4287 void QCPLayoutGrid::setRowStretchFactors(const QList<double> &factors)
4288 {
4289  if (factors.size() == mRowStretchFactors.size())
4290  {
4291  mRowStretchFactors = factors;
4292  for (int i=0; i<mRowStretchFactors.size(); ++i)
4293  {
4294  if (mRowStretchFactors.at(i) <= 0)
4295  {
4296  qDebug() << Q_FUNC_INFO << "Invalid stretch factor, must be positive:" << mRowStretchFactors.at(i);
4297  mRowStretchFactors[i] = 1;
4298  }
4299  }
4300  } else
4301  qDebug() << Q_FUNC_INFO << "Row count not equal to passed stretch factor count:" << factors;
4302 }
4303 
4310 {
4311  mColumnSpacing = pixels;
4312 }
4313 
4320 {
4321  mRowSpacing = pixels;
4322 }
4323 
4341 void QCPLayoutGrid::setWrap(int count)
4342 {
4343  mWrap = qMax(0, count);
4344 }
4345 
4370 void QCPLayoutGrid::setFillOrder(FillOrder order, bool rearrange)
4371 {
4372  // if rearranging, take all elements via linear index of old fill order:
4373  const int elCount = elementCount();
4374  QVector<QCPLayoutElement*> tempElements;
4375  if (rearrange)
4376  {
4377  tempElements.reserve(elCount);
4378  for (int i=0; i<elCount; ++i)
4379  {
4380  if (elementAt(i))
4381  tempElements.append(takeAt(i));
4382  }
4383  simplify();
4384  }
4385  // change fill order as requested:
4386  mFillOrder = order;
4387  // if rearranging, re-insert via linear index according to new fill order:
4388  if (rearrange)
4389  {
4390  for (int i=0; i<tempElements.size(); ++i)
4391  addElement(tempElements.at(i));
4392  }
4393 }
4394 
4409 void QCPLayoutGrid::expandTo(int newRowCount, int newColumnCount)
4410 {
4411  // add rows as necessary:
4412  while (rowCount() < newRowCount)
4413  {
4414  mElements.append(QList<QCPLayoutElement*>());
4415  mRowStretchFactors.append(1);
4416  }
4417  // go through rows and expand columns as necessary:
4418  int newColCount = qMax(columnCount(), newColumnCount);
4419  for (int i=0; i<rowCount(); ++i)
4420  {
4421  while (mElements.at(i).size() < newColCount)
4422  mElements[i].append(0);
4423  }
4424  while (mColumnStretchFactors.size() < newColCount)
4425  mColumnStretchFactors.append(1);
4426 }
4427 
4434 void QCPLayoutGrid::insertRow(int newIndex)
4435 {
4436  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
4437  {
4438  expandTo(1, 1);
4439  return;
4440  }
4441 
4442  if (newIndex < 0)
4443  newIndex = 0;
4444  if (newIndex > rowCount())
4445  newIndex = rowCount();
4446 
4447  mRowStretchFactors.insert(newIndex, 1);
4448  QList<QCPLayoutElement*> newRow;
4449  for (int col=0; col<columnCount(); ++col)
4450  newRow.append((QCPLayoutElement*)0);
4451  mElements.insert(newIndex, newRow);
4452 }
4453 
4462 {
4463  if (mElements.isEmpty() || mElements.first().isEmpty()) // if grid is completely empty, add first cell
4464  {
4465  expandTo(1, 1);
4466  return;
4467  }
4468 
4469  if (newIndex < 0)
4470  newIndex = 0;
4471  if (newIndex > columnCount())
4472  newIndex = columnCount();
4473 
4474  mColumnStretchFactors.insert(newIndex, 1);
4475  for (int row=0; row<rowCount(); ++row)
4476  mElements[row].insert(newIndex, (QCPLayoutElement*)0);
4477 }
4478 
4492 int QCPLayoutGrid::rowColToIndex(int row, int column) const
4493 {
4494  if (row >= 0 && row < rowCount())
4495  {
4496  if (column >= 0 && column < columnCount())
4497  {
4498  switch (mFillOrder)
4499  {
4500  case foRowsFirst: return column*rowCount() + row;
4501  case foColumnsFirst: return row*columnCount() + column;
4502  }
4503  } else
4504  qDebug() << Q_FUNC_INFO << "row index out of bounds:" << row;
4505  } else
4506  qDebug() << Q_FUNC_INFO << "column index out of bounds:" << column;
4507  return 0;
4508 }
4509 
4525 void QCPLayoutGrid::indexToRowCol(int index, int &row, int &column) const
4526 {
4527  row = -1;
4528  column = -1;
4529  const int nCols = columnCount();
4530  const int nRows = rowCount();
4531  if (nCols == 0 || nRows == 0)
4532  return;
4533  if (index < 0 || index >= elementCount())
4534  {
4535  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
4536  return;
4537  }
4538 
4539  switch (mFillOrder)
4540  {
4541  case foRowsFirst:
4542  {
4543  column = index / nRows;
4544  row = index % nRows;
4545  break;
4546  }
4547  case foColumnsFirst:
4548  {
4549  row = index / nCols;
4550  column = index % nCols;
4551  break;
4552  }
4553  }
4554 }
4555 
4556 /* inherits documentation from base class */
4558 {
4559  QVector<int> minColWidths, minRowHeights, maxColWidths, maxRowHeights;
4560  getMinimumRowColSizes(&minColWidths, &minRowHeights);
4561  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
4562 
4563  int totalRowSpacing = (rowCount()-1) * mRowSpacing;
4564  int totalColSpacing = (columnCount()-1) * mColumnSpacing;
4565  QVector<int> colWidths = getSectionSizes(maxColWidths, minColWidths, mColumnStretchFactors.toVector(), mRect.width()-totalColSpacing);
4566  QVector<int> rowHeights = getSectionSizes(maxRowHeights, minRowHeights, mRowStretchFactors.toVector(), mRect.height()-totalRowSpacing);
4567 
4568  // go through cells and set rects accordingly:
4569  int yOffset = mRect.top();
4570  for (int row=0; row<rowCount(); ++row)
4571  {
4572  if (row > 0)
4573  yOffset += rowHeights.at(row-1)+mRowSpacing;
4574  int xOffset = mRect.left();
4575  for (int col=0; col<columnCount(); ++col)
4576  {
4577  if (col > 0)
4578  xOffset += colWidths.at(col-1)+mColumnSpacing;
4579  if (mElements.at(row).at(col))
4580  mElements.at(row).at(col)->setOuterRect(QRect(xOffset, yOffset, colWidths.at(col), rowHeights.at(row)));
4581  }
4582  }
4583 }
4584 
4594 {
4595  if (index >= 0 && index < elementCount())
4596  {
4597  int row, col;
4598  indexToRowCol(index, row, col);
4599  return mElements.at(row).at(col);
4600  } else
4601  return 0;
4602 }
4603 
4613 {
4614  if (QCPLayoutElement *el = elementAt(index))
4615  {
4616  releaseElement(el);
4617  int row, col;
4618  indexToRowCol(index, row, col);
4619  mElements[row][col] = 0;
4620  return el;
4621  } else
4622  {
4623  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
4624  return 0;
4625  }
4626 }
4627 
4628 /* inherits documentation from base class */
4630 {
4631  if (element)
4632  {
4633  for (int i=0; i<elementCount(); ++i)
4634  {
4635  if (elementAt(i) == element)
4636  {
4637  takeAt(i);
4638  return true;
4639  }
4640  }
4641  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
4642  } else
4643  qDebug() << Q_FUNC_INFO << "Can't take null element";
4644  return false;
4645 }
4646 
4647 /* inherits documentation from base class */
4648 QList<QCPLayoutElement*> QCPLayoutGrid::elements(bool recursive) const
4649 {
4650  QList<QCPLayoutElement*> result;
4651  const int elCount = elementCount();
4652 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
4653  result.reserve(elCount);
4654 #endif
4655  for (int i=0; i<elCount; ++i)
4656  result.append(elementAt(i));
4657  if (recursive)
4658  {
4659  for (int i=0; i<elCount; ++i)
4660  {
4661  if (result.at(i))
4662  result << result.at(i)->elements(recursive);
4663  }
4664  }
4665  return result;
4666 }
4667 
4672 {
4673  // remove rows with only empty cells:
4674  for (int row=rowCount()-1; row>=0; --row)
4675  {
4676  bool hasElements = false;
4677  for (int col=0; col<columnCount(); ++col)
4678  {
4679  if (mElements.at(row).at(col))
4680  {
4681  hasElements = true;
4682  break;
4683  }
4684  }
4685  if (!hasElements)
4686  {
4687  mRowStretchFactors.removeAt(row);
4688  mElements.removeAt(row);
4689  if (mElements.isEmpty()) // removed last element, also remove stretch factor (wouldn't happen below because also columnCount changed to 0 now)
4690  mColumnStretchFactors.clear();
4691  }
4692  }
4693 
4694  // remove columns with only empty cells:
4695  for (int col=columnCount()-1; col>=0; --col)
4696  {
4697  bool hasElements = false;
4698  for (int row=0; row<rowCount(); ++row)
4699  {
4700  if (mElements.at(row).at(col))
4701  {
4702  hasElements = true;
4703  break;
4704  }
4705  }
4706  if (!hasElements)
4707  {
4708  mColumnStretchFactors.removeAt(col);
4709  for (int row=0; row<rowCount(); ++row)
4710  mElements[row].removeAt(col);
4711  }
4712  }
4713 }
4714 
4715 /* inherits documentation from base class */
4717 {
4718  QVector<int> minColWidths, minRowHeights;
4719  getMinimumRowColSizes(&minColWidths, &minRowHeights);
4720  QSize result(0, 0);
4721  for (int i=0; i<minColWidths.size(); ++i)
4722  result.rwidth() += minColWidths.at(i);
4723  for (int i=0; i<minRowHeights.size(); ++i)
4724  result.rheight() += minRowHeights.at(i);
4725  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing;
4726  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing;
4727  result.rwidth() += mMargins.left()+mMargins.right();
4728  result.rheight() += mMargins.top()+mMargins.bottom();
4729  return result;
4730 }
4731 
4732 /* inherits documentation from base class */
4734 {
4735  QVector<int> maxColWidths, maxRowHeights;
4736  getMaximumRowColSizes(&maxColWidths, &maxRowHeights);
4737 
4738  QSize result(0, 0);
4739  for (int i=0; i<maxColWidths.size(); ++i)
4740  result.setWidth(qMin(result.width()+maxColWidths.at(i), QWIDGETSIZE_MAX));
4741  for (int i=0; i<maxRowHeights.size(); ++i)
4742  result.setHeight(qMin(result.height()+maxRowHeights.at(i), QWIDGETSIZE_MAX));
4743  result.rwidth() += qMax(0, columnCount()-1) * mColumnSpacing;
4744  result.rheight() += qMax(0, rowCount()-1) * mRowSpacing;
4745  result.rwidth() += mMargins.left()+mMargins.right();
4746  result.rheight() += mMargins.top()+mMargins.bottom();
4747  if (result.height() > QWIDGETSIZE_MAX)
4748  result.setHeight(QWIDGETSIZE_MAX);
4749  if (result.width() > QWIDGETSIZE_MAX)
4750  result.setWidth(QWIDGETSIZE_MAX);
4751  return result;
4752 }
4753 
4767 void QCPLayoutGrid::getMinimumRowColSizes(QVector<int> *minColWidths, QVector<int> *minRowHeights) const
4768 {
4769  *minColWidths = QVector<int>(columnCount(), 0);
4770  *minRowHeights = QVector<int>(rowCount(), 0);
4771  for (int row=0; row<rowCount(); ++row)
4772  {
4773  for (int col=0; col<columnCount(); ++col)
4774  {
4775  if (QCPLayoutElement *el = mElements.at(row).at(col))
4776  {
4777  QSize minSize = getFinalMinimumOuterSize(el);
4778  if (minColWidths->at(col) < minSize.width())
4779  (*minColWidths)[col] = minSize.width();
4780  if (minRowHeights->at(row) < minSize.height())
4781  (*minRowHeights)[row] = minSize.height();
4782  }
4783  }
4784  }
4785 }
4786 
4800 void QCPLayoutGrid::getMaximumRowColSizes(QVector<int> *maxColWidths, QVector<int> *maxRowHeights) const
4801 {
4802  *maxColWidths = QVector<int>(columnCount(), QWIDGETSIZE_MAX);
4803  *maxRowHeights = QVector<int>(rowCount(), QWIDGETSIZE_MAX);
4804  for (int row=0; row<rowCount(); ++row)
4805  {
4806  for (int col=0; col<columnCount(); ++col)
4807  {
4808  if (QCPLayoutElement *el = mElements.at(row).at(col))
4809  {
4810  QSize maxSize = getFinalMaximumOuterSize(el);
4811  if (maxColWidths->at(col) > maxSize.width())
4812  (*maxColWidths)[col] = maxSize.width();
4813  if (maxRowHeights->at(row) > maxSize.height())
4814  (*maxRowHeights)[row] = maxSize.height();
4815  }
4816  }
4817  }
4818 }
4819 
4820 
4824 
4842 /* start documentation of inline functions */
4843 
4850 /* end documentation of inline functions */
4851 
4856 {
4857 }
4858 
4860 {
4861  // clear all child layout elements. This is important because only the specific layouts know how
4862  // to handle removing elements (clear calls virtual removeAt method to do that).
4863  clear();
4864 }
4865 
4870 {
4871  if (elementAt(index))
4872  return mInsetPlacement.at(index);
4873  else
4874  {
4875  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4876  return ipFree;
4877  }
4878 }
4879 
4884 Qt::Alignment QCPLayoutInset::insetAlignment(int index) const
4885 {
4886  if (elementAt(index))
4887  return mInsetAlignment.at(index);
4888  else
4889  {
4890  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4891  return 0;
4892  }
4893 }
4894 
4899 QRectF QCPLayoutInset::insetRect(int index) const
4900 {
4901  if (elementAt(index))
4902  return mInsetRect.at(index);
4903  else
4904  {
4905  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4906  return QRectF();
4907  }
4908 }
4909 
4916 {
4917  if (elementAt(index))
4918  mInsetPlacement[index] = placement;
4919  else
4920  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4921 }
4922 
4931 void QCPLayoutInset::setInsetAlignment(int index, Qt::Alignment alignment)
4932 {
4933  if (elementAt(index))
4934  mInsetAlignment[index] = alignment;
4935  else
4936  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4937 }
4938 
4950 void QCPLayoutInset::setInsetRect(int index, const QRectF &rect)
4951 {
4952  if (elementAt(index))
4953  mInsetRect[index] = rect;
4954  else
4955  qDebug() << Q_FUNC_INFO << "Invalid element index:" << index;
4956 }
4957 
4958 /* inherits documentation from base class */
4960 {
4961  for (int i=0; i<mElements.size(); ++i)
4962  {
4963  QCPLayoutElement *el = mElements.at(i);
4964  QRect insetRect;
4965  QSize finalMinSize = getFinalMinimumOuterSize(el);
4966  QSize finalMaxSize = getFinalMaximumOuterSize(el);
4967  if (mInsetPlacement.at(i) == ipFree)
4968  {
4969  insetRect = QRect(rect().x()+rect().width()*mInsetRect.at(i).x(),
4970  rect().y()+rect().height()*mInsetRect.at(i).y(),
4971  rect().width()*mInsetRect.at(i).width(),
4972  rect().height()*mInsetRect.at(i).height());
4973  if (insetRect.size().width() < finalMinSize.width())
4974  insetRect.setWidth(finalMinSize.width());
4975  if (insetRect.size().height() < finalMinSize.height())
4976  insetRect.setHeight(finalMinSize.height());
4977  if (insetRect.size().width() > finalMaxSize.width())
4978  insetRect.setWidth(finalMaxSize.width());
4979  if (insetRect.size().height() > finalMaxSize.height())
4980  insetRect.setHeight(finalMaxSize.height());
4981  } else if (mInsetPlacement.at(i) == ipBorderAligned)
4982  {
4983  insetRect.setSize(finalMinSize);
4984  Qt::Alignment al = mInsetAlignment.at(i);
4985  if (al.testFlag(Qt::AlignLeft)) insetRect.moveLeft(rect().x());
4986  else if (al.testFlag(Qt::AlignRight)) insetRect.moveRight(rect().x()+rect().width());
4987  else insetRect.moveLeft(rect().x()+rect().width()*0.5-finalMinSize.width()*0.5); // default to Qt::AlignHCenter
4988  if (al.testFlag(Qt::AlignTop)) insetRect.moveTop(rect().y());
4989  else if (al.testFlag(Qt::AlignBottom)) insetRect.moveBottom(rect().y()+rect().height());
4990  else insetRect.moveTop(rect().y()+rect().height()*0.5-finalMinSize.height()*0.5); // default to Qt::AlignVCenter
4991  }
4992  mElements.at(i)->setOuterRect(insetRect);
4993  }
4994 }
4995 
4996 /* inherits documentation from base class */
4998 {
4999  return mElements.size();
5000 }
5001 
5002 /* inherits documentation from base class */
5004 {
5005  if (index >= 0 && index < mElements.size())
5006  return mElements.at(index);
5007  else
5008  return 0;
5009 }
5010 
5011 /* inherits documentation from base class */
5013 {
5014  if (QCPLayoutElement *el = elementAt(index))
5015  {
5016  releaseElement(el);
5017  mElements.removeAt(index);
5018  mInsetPlacement.removeAt(index);
5019  mInsetAlignment.removeAt(index);
5020  mInsetRect.removeAt(index);
5021  return el;
5022  } else
5023  {
5024  qDebug() << Q_FUNC_INFO << "Attempt to take invalid index:" << index;
5025  return 0;
5026  }
5027 }
5028 
5029 /* inherits documentation from base class */
5031 {
5032  if (element)
5033  {
5034  for (int i=0; i<elementCount(); ++i)
5035  {
5036  if (elementAt(i) == element)
5037  {
5038  takeAt(i);
5039  return true;
5040  }
5041  }
5042  qDebug() << Q_FUNC_INFO << "Element not in this layout, couldn't take";
5043  } else
5044  qDebug() << Q_FUNC_INFO << "Can't take null element";
5045  return false;
5046 }
5047 
5057 double QCPLayoutInset::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
5058 {
5059  Q_UNUSED(details)
5060  if (onlySelectable)
5061  return -1;
5062 
5063  for (int i=0; i<mElements.size(); ++i)
5064  {
5065  // inset layout shall only return positive selectTest, if actually an inset object is at pos
5066  // else it would block the entire underlying QCPAxisRect with its surface.
5067  if (mElements.at(i)->realVisibility() && mElements.at(i)->selectTest(pos, onlySelectable) >= 0)
5068  return mParentPlot->selectionTolerance()*0.99;
5069  }
5070  return -1;
5071 }
5072 
5084 void QCPLayoutInset::addElement(QCPLayoutElement *element, Qt::Alignment alignment)
5085 {
5086  if (element)
5087  {
5088  if (element->layout()) // remove from old layout first
5089  element->layout()->take(element);
5090  mElements.append(element);
5091  mInsetPlacement.append(ipBorderAligned);
5092  mInsetAlignment.append(alignment);
5093  mInsetRect.append(QRectF(0.6, 0.6, 0.4, 0.4));
5094  adoptElement(element);
5095  } else
5096  qDebug() << Q_FUNC_INFO << "Can't add null element";
5097 }
5098 
5111 {
5112  if (element)
5113  {
5114  if (element->layout()) // remove from old layout first
5115  element->layout()->take(element);
5116  mElements.append(element);
5117  mInsetPlacement.append(ipFree);
5118  mInsetAlignment.append(Qt::AlignRight|Qt::AlignTop);
5119  mInsetRect.append(rect);
5120  adoptElement(element);
5121  } else
5122  qDebug() << Q_FUNC_INFO << "Can't add null element";
5123 }
5124 /* end of 'src/layout.cpp' */
5125 
5126 
5127 /* including file 'src/lineending.cpp', size 11536 */
5128 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
5129 
5133 
5158  mStyle(esNone),
5159  mWidth(8),
5160  mLength(10),
5161  mInverted(false)
5162 {
5163 }
5164 
5169  mStyle(style),
5170  mWidth(width),
5171  mLength(length),
5172  mInverted(inverted)
5173 {
5174 }
5175 
5180 {
5181  mStyle = style;
5182 }
5183 
5191 {
5192  mWidth = width;
5193 }
5194 
5202 {
5203  mLength = length;
5204 }
5205 
5215 {
5216  mInverted = inverted;
5217 }
5218 
5229 {
5230  switch (mStyle)
5231  {
5232  case esNone:
5233  return 0;
5234 
5235  case esFlatArrow:
5236  case esSpikeArrow:
5237  case esLineArrow:
5238  case esSkewedBar:
5239  return qSqrt(mWidth*mWidth+mLength*mLength); // items that have width and length
5240 
5241  case esDisc:
5242  case esSquare:
5243  case esDiamond:
5244  case esBar:
5245  case esHalfBar:
5246  return mWidth*1.42; // items that only have a width -> width*sqrt(2)
5247 
5248  }
5249  return 0;
5250 }
5251 
5264 {
5265  switch (mStyle)
5266  {
5267  case esNone:
5268  case esLineArrow:
5269  case esSkewedBar:
5270  case esBar:
5271  case esHalfBar:
5272  return 0;
5273 
5274  case esFlatArrow:
5275  return mLength;
5276 
5277  case esDisc:
5278  case esSquare:
5279  case esDiamond:
5280  return mWidth*0.5;
5281 
5282  case esSpikeArrow:
5283  return mLength*0.8;
5284  }
5285  return 0;
5286 }
5287 
5293 void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, const QCPVector2D &dir) const
5294 {
5295  if (mStyle == esNone)
5296  return;
5297 
5298  QCPVector2D lengthVec = dir.normalized() * mLength*(mInverted ? -1 : 1);
5299  if (lengthVec.isNull())
5300  lengthVec = QCPVector2D(1, 0);
5301  QCPVector2D widthVec = dir.normalized().perpendicular() * mWidth*0.5*(mInverted ? -1 : 1);
5302 
5303  QPen penBackup = painter->pen();
5304  QBrush brushBackup = painter->brush();
5305  QPen miterPen = penBackup;
5306  miterPen.setJoinStyle(Qt::MiterJoin); // to make arrow heads spikey
5307  QBrush brush(painter->pen().color(), Qt::SolidPattern);
5308  switch (mStyle)
5309  {
5310  case esNone: break;
5311  case esFlatArrow:
5312  {
5313  QPointF points[3] = {pos.toPointF(),
5314  (pos-lengthVec+widthVec).toPointF(),
5315  (pos-lengthVec-widthVec).toPointF()
5316  };
5317  painter->setPen(miterPen);
5318  painter->setBrush(brush);
5319  painter->drawConvexPolygon(points, 3);
5320  painter->setBrush(brushBackup);
5321  painter->setPen(penBackup);
5322  break;
5323  }
5324  case esSpikeArrow:
5325  {
5326  QPointF points[4] = {pos.toPointF(),
5327  (pos-lengthVec+widthVec).toPointF(),
5328  (pos-lengthVec*0.8).toPointF(),
5329  (pos-lengthVec-widthVec).toPointF()
5330  };
5331  painter->setPen(miterPen);
5332  painter->setBrush(brush);
5333  painter->drawConvexPolygon(points, 4);
5334  painter->setBrush(brushBackup);
5335  painter->setPen(penBackup);
5336  break;
5337  }
5338  case esLineArrow:
5339  {
5340  QPointF points[3] = {(pos-lengthVec+widthVec).toPointF(),
5341  pos.toPointF(),
5342  (pos-lengthVec-widthVec).toPointF()
5343  };
5344  painter->setPen(miterPen);
5345  painter->drawPolyline(points, 3);
5346  painter->setPen(penBackup);
5347  break;
5348  }
5349  case esDisc:
5350  {
5351  painter->setBrush(brush);
5352  painter->drawEllipse(pos.toPointF(), mWidth*0.5, mWidth*0.5);
5353  painter->setBrush(brushBackup);
5354  break;
5355  }
5356  case esSquare:
5357  {
5358  QCPVector2D widthVecPerp = widthVec.perpendicular();
5359  QPointF points[4] = {(pos-widthVecPerp+widthVec).toPointF(),
5360  (pos-widthVecPerp-widthVec).toPointF(),
5361  (pos+widthVecPerp-widthVec).toPointF(),
5362  (pos+widthVecPerp+widthVec).toPointF()
5363  };
5364  painter->setPen(miterPen);
5365  painter->setBrush(brush);
5366  painter->drawConvexPolygon(points, 4);
5367  painter->setBrush(brushBackup);
5368  painter->setPen(penBackup);
5369  break;
5370  }
5371  case esDiamond:
5372  {
5373  QCPVector2D widthVecPerp = widthVec.perpendicular();
5374  QPointF points[4] = {(pos-widthVecPerp).toPointF(),
5375  (pos-widthVec).toPointF(),
5376  (pos+widthVecPerp).toPointF(),
5377  (pos+widthVec).toPointF()
5378  };
5379  painter->setPen(miterPen);
5380  painter->setBrush(brush);
5381  painter->drawConvexPolygon(points, 4);
5382  painter->setBrush(brushBackup);
5383  painter->setPen(penBackup);
5384  break;
5385  }
5386  case esBar:
5387  {
5388  painter->drawLine((pos+widthVec).toPointF(), (pos-widthVec).toPointF());
5389  break;
5390  }
5391  case esHalfBar:
5392  {
5393  painter->drawLine((pos+widthVec).toPointF(), pos.toPointF());
5394  break;
5395  }
5396  case esSkewedBar:
5397  {
5398  if (qFuzzyIsNull(painter->pen().widthF()) && !painter->modes().testFlag(QCPPainter::pmNonCosmetic))
5399  {
5400  // if drawing with cosmetic pen (perfectly thin stroke, happens only in vector exports), draw bar exactly on tip of line
5401  painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)).toPointF(),
5402  (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)).toPointF());
5403  } else
5404  {
5405  // if drawing with thick (non-cosmetic) pen, shift bar a little in line direction to prevent line from sticking through bar slightly
5406  painter->drawLine((pos+widthVec+lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF(),
5407  (pos-widthVec-lengthVec*0.2*(mInverted?-1:1)+dir.normalized()*qMax(1.0f, (float)painter->pen().widthF())*0.5f).toPointF());
5408  }
5409  break;
5410  }
5411  }
5412 }
5413 
5419 void QCPLineEnding::draw(QCPPainter *painter, const QCPVector2D &pos, double angle) const
5420 {
5421  draw(painter, pos, QCPVector2D(qCos(angle), qSin(angle)));
5422 }
5423 /* end of 'src/lineending.cpp' */
5424 
5425 
5426 /* including file 'src/axis/axisticker.cpp', size 18664 */
5427 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
5428 
5432 
5490  mTickStepStrategy(tssReadability),
5491  mTickCount(5),
5492  mTickOrigin(0)
5493 {
5494 }
5495 
5497 {
5498 
5499 }
5500 
5506 {
5507  mTickStepStrategy = strategy;
5508 }
5509 
5519 {
5520  if (count > 0)
5521  mTickCount = count;
5522  else
5523  qDebug() << Q_FUNC_INFO << "tick count must be greater than zero:" << count;
5524 }
5525 
5534 void QCPAxisTicker::setTickOrigin(double origin)
5535 {
5536  mTickOrigin = origin;
5537 }
5538 
5552 void QCPAxisTicker::generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector<double> &ticks, QVector<double> *subTicks, QVector<QString> *tickLabels)
5553 {
5554  // generate (major) ticks:
5555  double tickStep = getTickStep(range);
5556  ticks = createTickVector(tickStep, range);
5557  trimTicks(range, ticks, true); // trim ticks to visible range plus one outer tick on each side (incase a subclass createTickVector creates more)
5558 
5559  // generate sub ticks between major ticks:
5560  if (subTicks)
5561  {
5562  if (ticks.size() > 0)
5563  {
5564  *subTicks = createSubTickVector(getSubTickCount(tickStep), ticks);
5565  trimTicks(range, *subTicks, false);
5566  } else
5567  *subTicks = QVector<double>();
5568  }
5569 
5570  // finally trim also outliers (no further clipping happens in axis drawing):
5571  trimTicks(range, ticks, false);
5572  // generate labels for visible ticks if requested:
5573  if (tickLabels)
5574  *tickLabels = createLabelVector(ticks, locale, formatChar, precision);
5575 }
5576 
5588 {
5589  double exactStep = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
5590  return cleanMantissa(exactStep);
5591 }
5592 
5601 {
5602  int result = 1; // default to 1, if no proper value can be found
5603 
5604  // separate integer and fractional part of mantissa:
5605  double epsilon = 0.01;
5606  double intPartf;
5607  int intPart;
5608  double fracPart = modf(getMantissa(tickStep), &intPartf);
5609  intPart = intPartf;
5610 
5611  // handle cases with (almost) integer mantissa:
5612  if (fracPart < epsilon || 1.0-fracPart < epsilon)
5613  {
5614  if (1.0-fracPart < epsilon)
5615  ++intPart;
5616  switch (intPart)
5617  {
5618  case 1: result = 4; break; // 1.0 -> 0.2 substep
5619  case 2: result = 3; break; // 2.0 -> 0.5 substep
5620  case 3: result = 2; break; // 3.0 -> 1.0 substep
5621  case 4: result = 3; break; // 4.0 -> 1.0 substep
5622  case 5: result = 4; break; // 5.0 -> 1.0 substep
5623  case 6: result = 2; break; // 6.0 -> 2.0 substep
5624  case 7: result = 6; break; // 7.0 -> 1.0 substep
5625  case 8: result = 3; break; // 8.0 -> 2.0 substep
5626  case 9: result = 2; break; // 9.0 -> 3.0 substep
5627  }
5628  } else
5629  {
5630  // handle cases with significantly fractional mantissa:
5631  if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
5632  {
5633  switch (intPart)
5634  {
5635  case 1: result = 2; break; // 1.5 -> 0.5 substep
5636  case 2: result = 4; break; // 2.5 -> 0.5 substep
5637  case 3: result = 4; break; // 3.5 -> 0.7 substep
5638  case 4: result = 2; break; // 4.5 -> 1.5 substep
5639  case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with default getTickStep from here on)
5640  case 6: result = 4; break; // 6.5 -> 1.3 substep
5641  case 7: result = 2; break; // 7.5 -> 2.5 substep
5642  case 8: result = 4; break; // 8.5 -> 1.7 substep
5643  case 9: result = 4; break; // 9.5 -> 1.9 substep
5644  }
5645  }
5646  // if mantissa fraction isn't 0.0 or 0.5, don't bother finding good sub tick marks, leave default
5647  }
5648 
5649  return result;
5650 }
5651 
5663 QString QCPAxisTicker::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
5664 {
5665  return locale.toString(tick, formatChar.toLatin1(), precision);
5666 }
5667 
5677 QVector<double> QCPAxisTicker::createSubTickVector(int subTickCount, const QVector<double> &ticks)
5678 {
5679  QVector<double> result;
5680  if (subTickCount <= 0 || ticks.size() < 2)
5681  return result;
5682 
5683  result.reserve((ticks.size()-1)*subTickCount);
5684  for (int i=1; i<ticks.size(); ++i)
5685  {
5686  double subTickStep = (ticks.at(i)-ticks.at(i-1))/(double)(subTickCount+1);
5687  for (int k=1; k<=subTickCount; ++k)
5688  result.append(ticks.at(i-1) + k*subTickStep);
5689  }
5690  return result;
5691 }
5692 
5708 QVector<double> QCPAxisTicker::createTickVector(double tickStep, const QCPRange &range)
5709 {
5710  QVector<double> result;
5711  // Generate tick positions according to tickStep:
5712  qint64 firstStep = floor((range.lower-mTickOrigin)/tickStep); // do not use qFloor here, or we'll lose 64 bit precision
5713  qint64 lastStep = ceil((range.upper-mTickOrigin)/tickStep); // do not use qCeil here, or we'll lose 64 bit precision
5714  int tickcount = lastStep-firstStep+1;
5715  if (tickcount < 0) tickcount = 0;
5716  result.resize(tickcount);
5717  for (int i=0; i<tickcount; ++i)
5718  result[i] = mTickOrigin + (firstStep+i)*tickStep;
5719  return result;
5720 }
5721 
5731 QVector<QString> QCPAxisTicker::createLabelVector(const QVector<double> &ticks, const QLocale &locale, QChar formatChar, int precision)
5732 {
5733  QVector<QString> result;
5734  result.reserve(ticks.size());
5735  for (int i=0; i<ticks.size(); ++i)
5736  result.append(getTickLabel(ticks.at(i), locale, formatChar, precision));
5737  return result;
5738 }
5739 
5747 void QCPAxisTicker::trimTicks(const QCPRange &range, QVector<double> &ticks, bool keepOneOutlier) const
5748 {
5749  bool lowFound = false;
5750  bool highFound = false;
5751  int lowIndex = 0;
5752  int highIndex = -1;
5753 
5754  for (int i=0; i < ticks.size(); ++i)
5755  {
5756  if (ticks.at(i) >= range.lower)
5757  {
5758  lowFound = true;
5759  lowIndex = i;
5760  break;
5761  }
5762  }
5763  for (int i=ticks.size()-1; i >= 0; --i)
5764  {
5765  if (ticks.at(i) <= range.upper)
5766  {
5767  highFound = true;
5768  highIndex = i;
5769  break;
5770  }
5771  }
5772 
5773  if (highFound && lowFound)
5774  {
5775  int trimFront = qMax(0, lowIndex-(keepOneOutlier ? 1 : 0));
5776  int trimBack = qMax(0, ticks.size()-(keepOneOutlier ? 2 : 1)-highIndex);
5777  if (trimFront > 0 || trimBack > 0)
5778  ticks = ticks.mid(trimFront, ticks.size()-trimFront-trimBack);
5779  } else // all ticks are either all below or all above the range
5780  ticks.clear();
5781 }
5782 
5789 double QCPAxisTicker::pickClosest(double target, const QVector<double> &candidates) const
5790 {
5791  if (candidates.size() == 1)
5792  return candidates.first();
5793  QVector<double>::const_iterator it = std::lower_bound(candidates.constBegin(), candidates.constEnd(), target);
5794  if (it == candidates.constEnd())
5795  return *(it-1);
5796  else if (it == candidates.constBegin())
5797  return *it;
5798  else
5799  return target-*(it-1) < *it-target ? *(it-1) : *it;
5800 }
5801 
5809 double QCPAxisTicker::getMantissa(double input, double *magnitude) const
5810 {
5811  const double mag = qPow(10.0, qFloor(qLn(input)/qLn(10.0)));
5812  if (magnitude) *magnitude = mag;
5813  return input/mag;
5814 }
5815 
5822 double QCPAxisTicker::cleanMantissa(double input) const
5823 {
5824  double magnitude;
5825  const double mantissa = getMantissa(input, &magnitude);
5826  switch (mTickStepStrategy)
5827  {
5828  case tssReadability:
5829  {
5830  return pickClosest(mantissa, QVector<double>() << 1.0 << 2.0 << 2.5 << 5.0 << 10.0)*magnitude;
5831  }
5832  case tssMeetTickCount:
5833  {
5834  // this gives effectively a mantissa of 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 8.0, 10.0
5835  if (mantissa <= 5.0)
5836  return (int)(mantissa*2)/2.0*magnitude; // round digit after decimal point to 0.5
5837  else
5838  return (int)(mantissa/2.0)*2.0*magnitude; // round to first digit in multiples of 2
5839  }
5840  }
5841  return input;
5842 }
5843 /* end of 'src/axis/axisticker.cpp' */
5844 
5845 
5846 /* including file 'src/axis/axistickerdatetime.cpp', size 14443 */
5847 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
5848 
5852 
5893  mDateTimeFormat(QLatin1String("hh:mm:ss\ndd.MM.yy")),
5894  mDateTimeSpec(Qt::LocalTime),
5895  mDateStrategy(dsNone)
5896 {
5897  setTickCount(4);
5898 }
5899 
5908 void QCPAxisTickerDateTime::setDateTimeFormat(const QString &format)
5909 {
5910  mDateTimeFormat = format;
5911 }
5912 
5924 {
5925  mDateTimeSpec = spec;
5926 }
5927 
5938 {
5940 }
5941 
5949 void QCPAxisTickerDateTime::setTickOrigin(const QDateTime &origin)
5950 {
5951  setTickOrigin(dateTimeToKey(origin));
5952 }
5953 
5968 {
5969  double result = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
5970 
5972  if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds
5973  {
5974  result = cleanMantissa(result);
5975  } else if (result < 86400*30.4375*12) // below a year
5976  {
5977  result = pickClosest(result, QVector<double>()
5978  << 1 << 2.5 << 5 << 10 << 15 << 30 << 60 << 2.5*60 << 5*60 << 10*60 << 15*60 << 30*60 << 60*60 // second, minute, hour range
5979  << 3600*2 << 3600*3 << 3600*6 << 3600*12 << 3600*24 // hour to day range
5980  << 86400*2 << 86400*5 << 86400*7 << 86400*14 << 86400*30.4375 << 86400*30.4375*2 << 86400*30.4375*3 << 86400*30.4375*6 << 86400*30.4375*12); // day, week, month range (avg. days per month includes leap years)
5981  if (result > 86400*30.4375-1) // month tick intervals or larger
5983  else if (result > 3600*24-1) // day tick intervals or larger
5985  } else // more than a year, go back to normal clean mantissa algorithm but in units of years
5986  {
5987  const double secondsPerYear = 86400*30.4375*12; // average including leap years
5988  result = cleanMantissa(result/secondsPerYear)*secondsPerYear;
5990  }
5991  return result;
5992 }
5993 
6002 {
6003  int result = QCPAxisTicker::getSubTickCount(tickStep);
6004  switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day/week/month range (as specified in getTickStep)
6005  {
6006  case 5*60: result = 4; break;
6007  case 10*60: result = 1; break;
6008  case 15*60: result = 2; break;
6009  case 30*60: result = 1; break;
6010  case 60*60: result = 3; break;
6011  case 3600*2: result = 3; break;
6012  case 3600*3: result = 2; break;
6013  case 3600*6: result = 1; break;
6014  case 3600*12: result = 3; break;
6015  case 3600*24: result = 3; break;
6016  case 86400*2: result = 1; break;
6017  case 86400*5: result = 4; break;
6018  case 86400*7: result = 6; break;
6019  case 86400*14: result = 1; break;
6020  case (int)(86400*30.4375+0.5): result = 3; break;
6021  case (int)(86400*30.4375*2+0.5): result = 1; break;
6022  case (int)(86400*30.4375*3+0.5): result = 2; break;
6023  case (int)(86400*30.4375*6+0.5): result = 5; break;
6024  case (int)(86400*30.4375*12+0.5): result = 3; break;
6025  }
6026  return result;
6027 }
6028 
6036 QString QCPAxisTickerDateTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
6037 {
6038  Q_UNUSED(precision)
6039  Q_UNUSED(formatChar)
6040  return locale.toString(keyToDateTime(tick).toTimeSpec(mDateTimeSpec), mDateTimeFormat);
6041 }
6042 
6050 QVector<double> QCPAxisTickerDateTime::createTickVector(double tickStep, const QCPRange &range)
6051 {
6052  QVector<double> result = QCPAxisTicker::createTickVector(tickStep, range);
6053  if (!result.isEmpty())
6054  {
6056  {
6057  QDateTime uniformDateTime = keyToDateTime(mTickOrigin); // the time of this datetime will be set for all other ticks, if possible
6058  QDateTime tickDateTime;
6059  for (int i=0; i<result.size(); ++i)
6060  {
6061  tickDateTime = keyToDateTime(result.at(i));
6062  tickDateTime.setTime(uniformDateTime.time());
6063  result[i] = dateTimeToKey(tickDateTime);
6064  }
6065  } else if (mDateStrategy == dsUniformDayInMonth)
6066  {
6067  QDateTime uniformDateTime = keyToDateTime(mTickOrigin); // this day (in month) and time will be set for all other ticks, if possible
6068  QDateTime tickDateTime;
6069  for (int i=0; i<result.size(); ++i)
6070  {
6071  tickDateTime = keyToDateTime(result.at(i));
6072  tickDateTime.setTime(uniformDateTime.time());
6073  int thisUniformDay = uniformDateTime.date().day() <= tickDateTime.date().daysInMonth() ? uniformDateTime.date().day() : tickDateTime.date().daysInMonth(); // don't exceed month (e.g. try to set day 31 in February)
6074  if (thisUniformDay-tickDateTime.date().day() < -15) // with leap years involved, date month may jump backwards or forwards, and needs to be corrected before setting day
6075  tickDateTime = tickDateTime.addMonths(1);
6076  else if (thisUniformDay-tickDateTime.date().day() > 15) // with leap years involved, date month may jump backwards or forwards, and needs to be corrected before setting day
6077  tickDateTime = tickDateTime.addMonths(-1);
6078  tickDateTime.setDate(QDate(tickDateTime.date().year(), tickDateTime.date().month(), thisUniformDay));
6079  result[i] = dateTimeToKey(tickDateTime);
6080  }
6081  }
6082  }
6083  return result;
6084 }
6085 
6096 {
6097 # if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
6098  return QDateTime::fromTime_t(key).addMSecs((key-(qint64)key)*1000);
6099 # else
6100  return QDateTime::fromMSecsSinceEpoch(key*1000.0);
6101 # endif
6102 }
6103 
6115 double QCPAxisTickerDateTime::dateTimeToKey(const QDateTime dateTime)
6116 {
6117 # if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
6118  return dateTime.toTime_t()+dateTime.time().msec()/1000.0;
6119 # else
6120  return dateTime.toMSecsSinceEpoch()/1000.0;
6121 # endif
6122 }
6123 
6133 {
6134 # if QT_VERSION < QT_VERSION_CHECK(4, 7, 0)
6135  return QDateTime(date).toTime_t();
6136 # else
6137  return QDateTime(date).toMSecsSinceEpoch()/1000.0;
6138 # endif
6139 }
6140 /* end of 'src/axis/axistickerdatetime.cpp' */
6141 
6142 
6143 /* including file 'src/axis/axistickertime.cpp', size 11747 */
6144 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
6145 
6149 
6190  mTimeFormat(QLatin1String("%h:%m:%s")),
6191  mSmallestUnit(tuSeconds),
6192  mBiggestUnit(tuHours)
6193 {
6194  setTickCount(4);
6196  mFieldWidth[tuSeconds] = 2;
6197  mFieldWidth[tuMinutes] = 2;
6198  mFieldWidth[tuHours] = 2;
6199  mFieldWidth[tuDays] = 1;
6200 
6201  mFormatPattern[tuMilliseconds] = QLatin1String("%z");
6202  mFormatPattern[tuSeconds] = QLatin1String("%s");
6203  mFormatPattern[tuMinutes] = QLatin1String("%m");
6204  mFormatPattern[tuHours] = QLatin1String("%h");
6205  mFormatPattern[tuDays] = QLatin1String("%d");
6206 }
6207 
6226 void QCPAxisTickerTime::setTimeFormat(const QString &format)
6227 {
6228  mTimeFormat = format;
6229 
6230  // determine smallest and biggest unit in format, to optimize unit replacement and allow biggest
6231  // unit to consume remaining time of a tick value and grow beyond its modulo (e.g. min > 59)
6234  bool hasSmallest = false;
6235  for (int i = tuMilliseconds; i <= tuDays; ++i)
6236  {
6237  TimeUnit unit = static_cast<TimeUnit>(i);
6238  if (mTimeFormat.contains(mFormatPattern.value(unit)))
6239  {
6240  if (!hasSmallest)
6241  {
6242  mSmallestUnit = unit;
6243  hasSmallest = true;
6244  }
6245  mBiggestUnit = unit;
6246  }
6247  }
6248 }
6249 
6258 {
6259  mFieldWidth[unit] = qMax(width, 1);
6260 }
6261 
6272 {
6273  double result = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
6274 
6275  if (result < 1) // ideal tick step is below 1 second -> use normal clean mantissa algorithm in units of seconds
6276  {
6278  result = qMax(cleanMantissa(result), 0.001); // smallest tick step is 1 millisecond
6279  else // have no milliseconds available in format, so stick with 1 second tickstep
6280  result = 1.0;
6281  } else if (result < 3600*24) // below a day
6282  {
6283  // the filling of availableSteps seems a bit contorted but it fills in a sorted fashion and thus saves a post-fill sorting run
6284  QVector<double> availableSteps;
6285  // seconds range:
6286  if (mSmallestUnit <= tuSeconds)
6287  availableSteps << 1;
6289  availableSteps << 2.5; // only allow half second steps if milliseconds are there to display it
6290  else if (mSmallestUnit == tuSeconds)
6291  availableSteps << 2;
6292  if (mSmallestUnit <= tuSeconds)
6293  availableSteps << 5 << 10 << 15 << 30;
6294  // minutes range:
6295  if (mSmallestUnit <= tuMinutes)
6296  availableSteps << 1*60;
6297  if (mSmallestUnit <= tuSeconds)
6298  availableSteps << 2.5*60; // only allow half minute steps if seconds are there to display it
6299  else if (mSmallestUnit == tuMinutes)
6300  availableSteps << 2*60;
6301  if (mSmallestUnit <= tuMinutes)
6302  availableSteps << 5*60 << 10*60 << 15*60 << 30*60;
6303  // hours range:
6304  if (mSmallestUnit <= tuHours)
6305  availableSteps << 1*3600 << 2*3600 << 3*3600 << 6*3600 << 12*3600 << 24*3600;
6306  // pick available step that is most appropriate to approximate ideal step:
6307  result = pickClosest(result, availableSteps);
6308  } else // more than a day, go back to normal clean mantissa algorithm but in units of days
6309  {
6310  const double secondsPerDay = 3600*24;
6311  result = cleanMantissa(result/secondsPerDay)*secondsPerDay;
6312  }
6313  return result;
6314 }
6315 
6323 {
6324  int result = QCPAxisTicker::getSubTickCount(tickStep);
6325  switch (qRound(tickStep)) // hand chosen subticks for specific minute/hour/day range (as specified in getTickStep)
6326  {
6327  case 5*60: result = 4; break;
6328  case 10*60: result = 1; break;
6329  case 15*60: result = 2; break;
6330  case 30*60: result = 1; break;
6331  case 60*60: result = 3; break;
6332  case 3600*2: result = 3; break;
6333  case 3600*3: result = 2; break;
6334  case 3600*6: result = 1; break;
6335  case 3600*12: result = 3; break;
6336  case 3600*24: result = 3; break;
6337  }
6338  return result;
6339 }
6340 
6348 QString QCPAxisTickerTime::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
6349 {
6350  Q_UNUSED(precision)
6351  Q_UNUSED(formatChar)
6352  Q_UNUSED(locale)
6353  bool negative = tick < 0;
6354  if (negative) tick *= -1;
6355  double values[tuDays+1]; // contains the msec/sec/min/... value with its respective modulo (e.g. minute 0..59)
6356  double restValues[tuDays+1]; // contains the msec/sec/min/... value as if it's the largest available unit and thus consumes the remaining time
6357 
6358  restValues[tuMilliseconds] = tick*1000;
6359  values[tuMilliseconds] = modf(restValues[tuMilliseconds]/1000, &restValues[tuSeconds])*1000;
6360  values[tuSeconds] = modf(restValues[tuSeconds]/60, &restValues[tuMinutes])*60;
6361  values[tuMinutes] = modf(restValues[tuMinutes]/60, &restValues[tuHours])*60;
6362  values[tuHours] = modf(restValues[tuHours]/24, &restValues[tuDays])*24;
6363  // no need to set values[tuDays] because days are always a rest value (there is no higher unit so it consumes all remaining time)
6364 
6365  QString result = mTimeFormat;
6366  for (int i = mSmallestUnit; i <= mBiggestUnit; ++i)
6367  {
6368  TimeUnit iUnit = static_cast<TimeUnit>(i);
6369  replaceUnit(result, iUnit, qRound(iUnit == mBiggestUnit ? restValues[iUnit] : values[iUnit]));
6370  }
6371  if (negative)
6372  result.prepend(QLatin1Char('-'));
6373  return result;
6374 }
6375 
6381 void QCPAxisTickerTime::replaceUnit(QString &text, QCPAxisTickerTime::TimeUnit unit, int value) const
6382 {
6383  QString valueStr = QString::number(value);
6384  while (valueStr.size() < mFieldWidth.value(unit))
6385  valueStr.prepend(QLatin1Char('0'));
6386 
6387  text.replace(mFormatPattern.value(unit), valueStr);
6388 }
6389 /* end of 'src/axis/axistickertime.cpp' */
6390 
6391 
6392 /* including file 'src/axis/axistickerfixed.cpp', size 5583 */
6393 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
6394 
6398 
6423  mTickStep(1.0),
6424  mScaleStrategy(ssNone)
6425 {
6426 }
6427 
6438 {
6439  if (step > 0)
6440  mTickStep = step;
6441  else
6442  qDebug() << Q_FUNC_INFO << "tick step must be greater than zero:" << step;
6443 }
6444 
6453 {
6454  mScaleStrategy = strategy;
6455 }
6456 
6468 {
6469  switch (mScaleStrategy)
6470  {
6471  case ssNone:
6472  {
6473  return mTickStep;
6474  }
6475  case ssMultiples:
6476  {
6477  double exactStep = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
6478  if (exactStep < mTickStep)
6479  return mTickStep;
6480  else
6481  return (qint64)(cleanMantissa(exactStep/mTickStep)+0.5)*mTickStep;
6482  }
6483  case ssPowers:
6484  {
6485  double exactStep = range.size()/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
6486  return qPow(mTickStep, (int)(qLn(exactStep)/qLn(mTickStep)+0.5));
6487  }
6488  }
6489  return mTickStep;
6490 }
6491 /* end of 'src/axis/axistickerfixed.cpp' */
6492 
6493 
6494 /* including file 'src/axis/axistickertext.cpp', size 8653 */
6495 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
6496 
6500 
6520 /* start of documentation of inline functions */
6521 
6531 /* end of documentation of inline functions */
6532 
6538  mSubTickCount(0)
6539 {
6540 }
6541 
6552 void QCPAxisTickerText::setTicks(const QMap<double, QString> &ticks)
6553 {
6554  mTicks = ticks;
6555 }
6556 
6565 void QCPAxisTickerText::setTicks(const QVector<double> &positions, const QVector<QString> labels)
6566 {
6567  clear();
6568  addTicks(positions, labels);
6569 }
6570 
6577 {
6578  if (subTicks >= 0)
6579  mSubTickCount = subTicks;
6580  else
6581  qDebug() << Q_FUNC_INFO << "sub tick count can't be negative:" << subTicks;
6582 }
6583 
6593 {
6594  mTicks.clear();
6595 }
6596 
6603 void QCPAxisTickerText::addTick(double position, QString label)
6604 {
6605  mTicks.insert(position, label);
6606 }
6607 
6618 void QCPAxisTickerText::addTicks(const QMap<double, QString> &ticks)
6619 {
6620  mTicks.unite(ticks);
6621 }
6622 
6634 void QCPAxisTickerText::addTicks(const QVector<double> &positions, const QVector<QString> &labels)
6635 {
6636  if (positions.size() != labels.size())
6637  qDebug() << Q_FUNC_INFO << "passed unequal length vectors for positions and labels:" << positions.size() << labels.size();
6638  int n = qMin(positions.size(), labels.size());
6639  for (int i=0; i<n; ++i)
6640  mTicks.insert(positions.at(i), labels.at(i));
6641 }
6642 
6649 {
6650  // text axis ticker has manual tick positions, so doesn't need this method
6651  Q_UNUSED(range)
6652  return 1.0;
6653 }
6654 
6661 {
6662  Q_UNUSED(tickStep)
6663  return mSubTickCount;
6664 }
6665 
6672 QString QCPAxisTickerText::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
6673 {
6674  Q_UNUSED(locale)
6675  Q_UNUSED(formatChar)
6676  Q_UNUSED(precision)
6677  return mTicks.value(tick);
6678 }
6679 
6687 QVector<double> QCPAxisTickerText::createTickVector(double tickStep, const QCPRange &range)
6688 {
6689  Q_UNUSED(tickStep)
6690  QVector<double> result;
6691  if (mTicks.isEmpty())
6692  return result;
6693 
6694  QMap<double, QString>::const_iterator start = mTicks.lowerBound(range.lower);
6695  QMap<double, QString>::const_iterator end = mTicks.upperBound(range.upper);
6696  // this method should try to give one tick outside of range so proper subticks can be generated:
6697  if (start != mTicks.constBegin()) --start;
6698  if (end != mTicks.constEnd()) ++end;
6699  for (QMap<double, QString>::const_iterator it = start; it != end; ++it)
6700  result.append(it.key());
6701 
6702  return result;
6703 }
6704 /* end of 'src/axis/axistickertext.cpp' */
6705 
6706 
6707 /* including file 'src/axis/axistickerpi.cpp', size 11170 */
6708 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
6709 
6713 
6734  mPiSymbol(QLatin1String(" ")+QChar(0x03C0)),
6735  mPiValue(M_PI),
6736  mPeriodicity(0),
6737  mFractionStyle(fsUnicodeFractions),
6738  mPiTickStep(0)
6739 {
6740  setTickCount(4);
6741 }
6742 
6750 void QCPAxisTickerPi::setPiSymbol(QString symbol)
6751 {
6752  mPiSymbol = symbol;
6753 }
6754 
6762 {
6763  mPiValue = pi;
6764 }
6765 
6774 void QCPAxisTickerPi::setPeriodicity(int multiplesOfPi)
6775 {
6776  mPeriodicity = qAbs(multiplesOfPi);
6777 }
6778 
6784 {
6785  mFractionStyle = style;
6786 }
6787 
6797 {
6798  mPiTickStep = range.size()/mPiValue/(double)(mTickCount+1e-10); // mTickCount ticks on average, the small addition is to prevent jitter on exact integers
6800  return mPiTickStep*mPiValue;
6801 }
6802 
6812 {
6813  return QCPAxisTicker::getSubTickCount(tickStep/mPiValue);
6814 }
6815 
6824 QString QCPAxisTickerPi::getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
6825 {
6826  double tickInPis = tick/mPiValue;
6827  if (mPeriodicity > 0)
6828  tickInPis = fmod(tickInPis, mPeriodicity);
6829 
6830  if (mFractionStyle != fsFloatingPoint && mPiTickStep > 0.09 && mPiTickStep < 50)
6831  {
6832  // simply construct fraction from decimal like 1.234 -> 1234/1000 and then simplify fraction, smaller digits are irrelevant due to mPiTickStep conditional above
6833  int denominator = 1000;
6834  int numerator = qRound(tickInPis*denominator);
6835  simplifyFraction(numerator, denominator);
6836  if (qAbs(numerator) == 1 && denominator == 1)
6837  return (numerator < 0 ? QLatin1String("-") : QLatin1String("")) + mPiSymbol.trimmed();
6838  else if (numerator == 0)
6839  return QLatin1String("0");
6840  else
6841  return fractionToString(numerator, denominator) + mPiSymbol;
6842  } else
6843  {
6844  if (qFuzzyIsNull(tickInPis))
6845  return QLatin1String("0");
6846  else if (qFuzzyCompare(qAbs(tickInPis), 1.0))
6847  return (tickInPis < 0 ? QLatin1String("-") : QLatin1String("")) + mPiSymbol.trimmed();
6848  else
6849  return QCPAxisTicker::getTickLabel(tickInPis, locale, formatChar, precision) + mPiSymbol;
6850  }
6851 }
6852 
6859 void QCPAxisTickerPi::simplifyFraction(int &numerator, int &denominator) const
6860 {
6861  if (numerator == 0 || denominator == 0)
6862  return;
6863 
6864  int num = numerator;
6865  int denom = denominator;
6866  while (denom != 0) // euclidean gcd algorithm
6867  {
6868  int oldDenom = denom;
6869  denom = num % denom;
6870  num = oldDenom;
6871  }
6872  // num is now gcd of numerator and denominator
6873  numerator /= num;
6874  denominator /= num;
6875 }
6876 
6886 QString QCPAxisTickerPi::fractionToString(int numerator, int denominator) const
6887 {
6888  if (denominator == 0)
6889  {
6890  qDebug() << Q_FUNC_INFO << "called with zero denominator";
6891  return QString();
6892  }
6893  if (mFractionStyle == fsFloatingPoint) // should never be the case when calling this function
6894  {
6895  qDebug() << Q_FUNC_INFO << "shouldn't be called with fraction style fsDecimal";
6896  return QString::number(numerator/(double)denominator); // failsafe
6897  }
6898  int sign = numerator*denominator < 0 ? -1 : 1;
6899  numerator = qAbs(numerator);
6900  denominator = qAbs(denominator);
6901 
6902  if (denominator == 1)
6903  {
6904  return QString::number(sign*numerator);
6905  } else
6906  {
6907  int integerPart = numerator/denominator;
6908  int remainder = numerator%denominator;
6909  if (remainder == 0)
6910  {
6911  return QString::number(sign*integerPart);
6912  } else
6913  {
6915  {
6916  return QString(QLatin1String("%1%2%3/%4"))
6917  .arg(sign == -1 ? QLatin1String("-") : QLatin1String(""))
6918  .arg(integerPart > 0 ? QString::number(integerPart)+QLatin1String(" ") : QLatin1String(""))
6919  .arg(remainder)
6920  .arg(denominator);
6921  } else if (mFractionStyle == fsUnicodeFractions)
6922  {
6923  return QString(QLatin1String("%1%2%3"))
6924  .arg(sign == -1 ? QLatin1String("-") : QLatin1String(""))
6925  .arg(integerPart > 0 ? QString::number(integerPart) : QLatin1String(""))
6926  .arg(unicodeFraction(remainder, denominator));
6927  }
6928  }
6929  }
6930  return QString();
6931 }
6932 
6942 QString QCPAxisTickerPi::unicodeFraction(int numerator, int denominator) const
6943 {
6944  return unicodeSuperscript(numerator)+QChar(0x2044)+unicodeSubscript(denominator);
6945 }
6946 
6952 QString QCPAxisTickerPi::unicodeSuperscript(int number) const
6953 {
6954  if (number == 0)
6955  return QString(QChar(0x2070));
6956 
6957  QString result;
6958  while (number > 0)
6959  {
6960  const int digit = number%10;
6961  switch (digit)
6962  {
6963  case 1: { result.prepend(QChar(0x00B9)); break; }
6964  case 2: { result.prepend(QChar(0x00B2)); break; }
6965  case 3: { result.prepend(QChar(0x00B3)); break; }
6966  default: { result.prepend(QChar(0x2070+digit)); break; }
6967  }
6968  number /= 10;
6969  }
6970  return result;
6971 }
6972 
6978 QString QCPAxisTickerPi::unicodeSubscript(int number) const
6979 {
6980  if (number == 0)
6981  return QString(QChar(0x2080));
6982 
6983  QString result;
6984  while (number > 0)
6985  {
6986  result.prepend(QChar(0x2080+number%10));
6987  number /= 10;
6988  }
6989  return result;
6990 }
6991 /* end of 'src/axis/axistickerpi.cpp' */
6992 
6993 
6994 /* including file 'src/axis/axistickerlog.cpp', size 7106 */
6995 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
6996 
7000 
7025  mLogBase(10.0),
7026  mSubTickCount(8), // generates 10 intervals
7027  mLogBaseLnInv(1.0/qLn(mLogBase))
7028 {
7029 }
7030 
7036 {
7037  if (base > 0)
7038  {
7039  mLogBase = base;
7040  mLogBaseLnInv = 1.0/qLn(mLogBase);
7041  } else
7042  qDebug() << Q_FUNC_INFO << "log base has to be greater than zero:" << base;
7043 }
7044 
7056 {
7057  if (subTicks >= 0)
7058  mSubTickCount = subTicks;
7059  else
7060  qDebug() << Q_FUNC_INFO << "sub tick count can't be negative:" << subTicks;
7061 }
7062 
7071 {
7072  // Logarithmic axis ticker has unequal tick spacing, so doesn't need this method
7073  Q_UNUSED(range)
7074  return 1.0;
7075 }
7076 
7085 {
7086  Q_UNUSED(tickStep)
7087  return mSubTickCount;
7088 }
7089 
7099 QVector<double> QCPAxisTickerLog::createTickVector(double tickStep, const QCPRange &range)
7100 {
7101  Q_UNUSED(tickStep)
7102  QVector<double> result;
7103  if (range.lower > 0 && range.upper > 0) // positive range
7104  {
7105  double exactPowerStep = qLn(range.upper/range.lower)*mLogBaseLnInv/(double)(mTickCount+1e-10);
7106  double newLogBase = qPow(mLogBase, qMax((int)cleanMantissa(exactPowerStep), 1));
7107  double currentTick = qPow(newLogBase, qFloor(qLn(range.lower)/qLn(newLogBase)));
7108  result.append(currentTick);
7109  while (currentTick < range.upper && currentTick > 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
7110  {
7111  currentTick *= newLogBase;
7112  result.append(currentTick);
7113  }
7114  } else if (range.lower < 0 && range.upper < 0) // negative range
7115  {
7116  double exactPowerStep = qLn(range.lower/range.upper)*mLogBaseLnInv/(double)(mTickCount+1e-10);
7117  double newLogBase = qPow(mLogBase, qMax((int)cleanMantissa(exactPowerStep), 1));
7118  double currentTick = -qPow(newLogBase, qCeil(qLn(-range.lower)/qLn(newLogBase)));
7119  result.append(currentTick);
7120  while (currentTick < range.upper && currentTick < 0) // currentMag might be zero for ranges ~1e-300, just cancel in that case
7121  {
7122  currentTick /= newLogBase;
7123  result.append(currentTick);
7124  }
7125  } else // invalid range for logarithmic scale, because lower and upper have different sign
7126  {
7127  qDebug() << Q_FUNC_INFO << "Invalid range for logarithmic plot: " << range.lower << ".." << range.upper;
7128  }
7129 
7130  return result;
7131 }
7132 /* end of 'src/axis/axistickerlog.cpp' */
7133 
7134 
7135 /* including file 'src/axis/axis.cpp', size 99397 */
7136 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
7137 
7138 
7142 
7162  QCPLayerable(parentAxis->parentPlot(), QString(), parentAxis),
7163  mParentAxis(parentAxis)
7164 {
7165  // warning: this is called in QCPAxis constructor, so parentAxis members should not be accessed/called
7166  setParent(parentAxis);
7167  setPen(QPen(QColor(200,200,200), 0, Qt::DotLine));
7168  setSubGridPen(QPen(QColor(220,220,220), 0, Qt::DotLine));
7169  setZeroLinePen(QPen(QColor(200,200,200), 0, Qt::SolidLine));
7170  setSubGridVisible(false);
7171  setAntialiased(false);
7172  setAntialiasedSubGrid(false);
7173  setAntialiasedZeroLine(false);
7174 }
7175 
7182 {
7184 }
7185 
7190 {
7191  mAntialiasedSubGrid = enabled;
7192 }
7193 
7198 {
7199  mAntialiasedZeroLine = enabled;
7200 }
7201 
7205 void QCPGrid::setPen(const QPen &pen)
7206 {
7207  mPen = pen;
7208 }
7209 
7213 void QCPGrid::setSubGridPen(const QPen &pen)
7214 {
7215  mSubGridPen = pen;
7216 }
7217 
7224 void QCPGrid::setZeroLinePen(const QPen &pen)
7225 {
7226  mZeroLinePen = pen;
7227 }
7228 
7243 {
7245 }
7246 
7253 {
7254  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
7255 
7257  drawSubGridLines(painter);
7258  drawGridLines(painter);
7259 }
7260 
7268 {
7269  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
7270 
7271  const int tickCount = mParentAxis->mTickVector.size();
7272  double t; // helper variable, result of coordinate-to-pixel transforms
7273  if (mParentAxis->orientation() == Qt::Horizontal)
7274  {
7275  // draw zeroline:
7276  int zeroLineIndex = -1;
7277  if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
7278  {
7280  painter->setPen(mZeroLinePen);
7281  double epsilon = mParentAxis->range().size()*1E-6; // for comparing double to zero
7282  for (int i=0; i<tickCount; ++i)
7283  {
7284  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
7285  {
7286  zeroLineIndex = i;
7287  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
7288  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
7289  break;
7290  }
7291  }
7292  }
7293  // draw grid lines:
7295  painter->setPen(mPen);
7296  for (int i=0; i<tickCount; ++i)
7297  {
7298  if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
7299  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // x
7300  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
7301  }
7302  } else
7303  {
7304  // draw zeroline:
7305  int zeroLineIndex = -1;
7306  if (mZeroLinePen.style() != Qt::NoPen && mParentAxis->mRange.lower < 0 && mParentAxis->mRange.upper > 0)
7307  {
7309  painter->setPen(mZeroLinePen);
7310  double epsilon = mParentAxis->mRange.size()*1E-6; // for comparing double to zero
7311  for (int i=0; i<tickCount; ++i)
7312  {
7313  if (qAbs(mParentAxis->mTickVector.at(i)) < epsilon)
7314  {
7315  zeroLineIndex = i;
7316  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
7317  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
7318  break;
7319  }
7320  }
7321  }
7322  // draw grid lines:
7324  painter->setPen(mPen);
7325  for (int i=0; i<tickCount; ++i)
7326  {
7327  if (i == zeroLineIndex) continue; // don't draw a gridline on top of the zeroline
7328  t = mParentAxis->coordToPixel(mParentAxis->mTickVector.at(i)); // y
7329  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
7330  }
7331  }
7332 }
7333 
7341 {
7342  if (!mParentAxis) { qDebug() << Q_FUNC_INFO << "invalid parent axis"; return; }
7343 
7345  double t; // helper variable, result of coordinate-to-pixel transforms
7346  painter->setPen(mSubGridPen);
7347  if (mParentAxis->orientation() == Qt::Horizontal)
7348  {
7349  for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
7350  {
7352  painter->drawLine(QLineF(t, mParentAxis->mAxisRect->bottom(), t, mParentAxis->mAxisRect->top()));
7353  }
7354  } else
7355  {
7356  for (int i=0; i<mParentAxis->mSubTickVector.size(); ++i)
7357  {
7359  painter->drawLine(QLineF(mParentAxis->mAxisRect->left(), t, mParentAxis->mAxisRect->right(), t));
7360  }
7361  }
7362 }
7363 
7364 
7368 
7391 /* start of documentation of inline functions */
7392 
7449 /* end of documentation of inline functions */
7450 /* start of documentation of signals */
7451 
7490 /* end of documentation of signals */
7491 
7500  QCPLayerable(parent->parentPlot(), QString(), parent),
7501  // axis base:
7502  mAxisType(type),
7503  mAxisRect(parent),
7504  mPadding(5),
7505  mOrientation(orientation(type)),
7506  mSelectableParts(spAxis | spTickLabels | spAxisLabel),
7507  mSelectedParts(spNone),
7508  mBasePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
7509  mSelectedBasePen(QPen(Qt::blue, 2)),
7510  // axis label:
7511  mLabel(),
7512  mLabelFont(mParentPlot->font()),
7513  mSelectedLabelFont(QFont(mLabelFont.family(), mLabelFont.pointSize(), QFont::Bold)),
7514  mLabelColor(Qt::black),
7515  mSelectedLabelColor(Qt::blue),
7516  // tick labels:
7517  mTickLabels(true),
7518  mTickLabelFont(mParentPlot->font()),
7519  mSelectedTickLabelFont(QFont(mTickLabelFont.family(), mTickLabelFont.pointSize(), QFont::Bold)),
7520  mTickLabelColor(Qt::black),
7521  mSelectedTickLabelColor(Qt::blue),
7522  mNumberPrecision(6),
7523  mNumberFormatChar('g'),
7524  mNumberBeautifulPowers(true),
7525  // ticks and subticks:
7526  mTicks(true),
7527  mSubTicks(true),
7528  mTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
7529  mSelectedTickPen(QPen(Qt::blue, 2)),
7530  mSubTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
7531  mSelectedSubTickPen(QPen(Qt::blue, 2)),
7532  // scale and range:
7533  mRange(0, 5),
7534  mRangeReversed(false),
7535  mScaleType(stLinear),
7536  // internal members:
7537  mGrid(new QCPGrid(this)),
7538  mAxisPainter(new QCPAxisPainterPrivate(parent->parentPlot())),
7539  mTicker(new QCPAxisTicker),
7540  mCachedMarginValid(false),
7541  mCachedMargin(0)
7542 {
7543  setParent(parent);
7544  mGrid->setVisible(false);
7545  setAntialiased(false);
7546  setLayer(mParentPlot->currentLayer()); // it's actually on that layer already, but we want it in front of the grid, so we place it on there again
7547 
7548  if (type == atTop)
7549  {
7551  setLabelPadding(6);
7552  } else if (type == atRight)
7553  {
7555  setLabelPadding(12);
7556  } else if (type == atBottom)
7557  {
7559  setLabelPadding(3);
7560  } else if (type == atLeft)
7561  {
7563  setLabelPadding(10);
7564  }
7565 }
7566 
7568 {
7569  delete mAxisPainter;
7570  delete mGrid; // delete grid here instead of via parent ~QObject for better defined deletion order
7571 }
7572 
7573 /* No documentation as it is a property getter */
7575 {
7577 }
7578 
7579 /* No documentation as it is a property getter */
7581 {
7583 }
7584 
7585 /* No documentation as it is a property getter */
7587 {
7588  return mAxisPainter->tickLabelSide;
7589 }
7590 
7591 /* No documentation as it is a property getter */
7592 QString QCPAxis::numberFormat() const
7593 {
7594  QString result;
7595  result.append(mNumberFormatChar);
7597  {
7598  result.append(QLatin1Char('b'));
7600  result.append(QLatin1Char('c'));
7601  }
7602  return result;
7603 }
7604 
7605 /* No documentation as it is a property getter */
7607 {
7608  return mAxisPainter->tickLengthIn;
7609 }
7610 
7611 /* No documentation as it is a property getter */
7613 {
7614  return mAxisPainter->tickLengthOut;
7615 }
7616 
7617 /* No documentation as it is a property getter */
7619 {
7620  return mAxisPainter->subTickLengthIn;
7621 }
7622 
7623 /* No documentation as it is a property getter */
7625 {
7627 }
7628 
7629 /* No documentation as it is a property getter */
7631 {
7632  return mAxisPainter->labelPadding;
7633 }
7634 
7635 /* No documentation as it is a property getter */
7636 int QCPAxis::offset() const
7637 {
7638  return mAxisPainter->offset;
7639 }
7640 
7641 /* No documentation as it is a property getter */
7643 {
7644  return mAxisPainter->lowerEnding;
7645 }
7646 
7647 /* No documentation as it is a property getter */
7649 {
7650  return mAxisPainter->upperEnding;
7651 }
7652 
7664 {
7665  if (mScaleType != type)
7666  {
7667  mScaleType = type;
7668  if (mScaleType == stLogarithmic)
7670  mCachedMarginValid = false;
7672  }
7673 }
7674 
7684 {
7685  if (range.lower == mRange.lower && range.upper == mRange.upper)
7686  return;
7687 
7688  if (!QCPRange::validRange(range)) return;
7689  QCPRange oldRange = mRange;
7690  if (mScaleType == stLogarithmic)
7691  {
7692  mRange = range.sanitizedForLogScale();
7693  } else
7694  {
7695  mRange = range.sanitizedForLinScale();
7696  }
7697  emit rangeChanged(mRange);
7698  emit rangeChanged(mRange, oldRange);
7699 }
7700 
7711 void QCPAxis::setSelectableParts(const SelectableParts &selectable)
7712 {
7713  if (mSelectableParts != selectable)
7714  {
7715  mSelectableParts = selectable;
7717  }
7718 }
7719 
7735 void QCPAxis::setSelectedParts(const SelectableParts &selected)
7736 {
7737  if (mSelectedParts != selected)
7738  {
7739  mSelectedParts = selected;
7741  }
7742 }
7743 
7753 void QCPAxis::setRange(double lower, double upper)
7754 {
7755  if (lower == mRange.lower && upper == mRange.upper)
7756  return;
7757 
7758  if (!QCPRange::validRange(lower, upper)) return;
7759  QCPRange oldRange = mRange;
7760  mRange.lower = lower;
7761  mRange.upper = upper;
7762  if (mScaleType == stLogarithmic)
7763  {
7765  } else
7766  {
7768  }
7769  emit rangeChanged(mRange);
7770  emit rangeChanged(mRange, oldRange);
7771 }
7772 
7784 void QCPAxis::setRange(double position, double size, Qt::AlignmentFlag alignment)
7785 {
7786  if (alignment == Qt::AlignLeft)
7787  setRange(position, position+size);
7788  else if (alignment == Qt::AlignRight)
7789  setRange(position-size, position);
7790  else // alignment == Qt::AlignCenter
7791  setRange(position-size/2.0, position+size/2.0);
7792 }
7793 
7798 void QCPAxis::setRangeLower(double lower)
7799 {
7800  if (mRange.lower == lower)
7801  return;
7802 
7803  QCPRange oldRange = mRange;
7804  mRange.lower = lower;
7805  if (mScaleType == stLogarithmic)
7806  {
7808  } else
7809  {
7811  }
7812  emit rangeChanged(mRange);
7813  emit rangeChanged(mRange, oldRange);
7814 }
7815 
7820 void QCPAxis::setRangeUpper(double upper)
7821 {
7822  if (mRange.upper == upper)
7823  return;
7824 
7825  QCPRange oldRange = mRange;
7826  mRange.upper = upper;
7827  if (mScaleType == stLogarithmic)
7828  {
7830  } else
7831  {
7833  }
7834  emit rangeChanged(mRange);
7835  emit rangeChanged(mRange, oldRange);
7836 }
7837 
7847 void QCPAxis::setRangeReversed(bool reversed)
7848 {
7849  mRangeReversed = reversed;
7850 }
7851 
7865 void QCPAxis::setTicker(QSharedPointer<QCPAxisTicker> ticker)
7866 {
7867  if (ticker)
7868  mTicker = ticker;
7869  else
7870  qDebug() << Q_FUNC_INFO << "can not set 0 as axis ticker";
7871  // no need to invalidate margin cache here because produced tick labels are checked for changes in setupTickVector
7872 }
7873 
7882 void QCPAxis::setTicks(bool show)
7883 {
7884  if (mTicks != show)
7885  {
7886  mTicks = show;
7887  mCachedMarginValid = false;
7888  }
7889 }
7890 
7894 void QCPAxis::setTickLabels(bool show)
7895 {
7896  if (mTickLabels != show)
7897  {
7898  mTickLabels = show;
7899  mCachedMarginValid = false;
7900  if (!mTickLabels)
7901  mTickVectorLabels.clear();
7902  }
7903 }
7904 
7910 {
7911  if (mAxisPainter->tickLabelPadding != padding)
7912  {
7914  mCachedMarginValid = false;
7915  }
7916 }
7917 
7923 void QCPAxis::setTickLabelFont(const QFont &font)
7924 {
7925  if (font != mTickLabelFont)
7926  {
7927  mTickLabelFont = font;
7928  mCachedMarginValid = false;
7929  }
7930 }
7931 
7937 void QCPAxis::setTickLabelColor(const QColor &color)
7938 {
7939  mTickLabelColor = color;
7940 }
7941 
7951 void QCPAxis::setTickLabelRotation(double degrees)
7952 {
7953  if (!qFuzzyIsNull(degrees-mAxisPainter->tickLabelRotation))
7954  {
7955  mAxisPainter->tickLabelRotation = qBound(-90.0, degrees, 90.0);
7956  mCachedMarginValid = false;
7957  }
7958 }
7959 
7968 {
7969  mAxisPainter->tickLabelSide = side;
7970  mCachedMarginValid = false;
7971 }
7972 
8003 void QCPAxis::setNumberFormat(const QString &formatCode)
8004 {
8005  if (formatCode.isEmpty())
8006  {
8007  qDebug() << Q_FUNC_INFO << "Passed formatCode is empty";
8008  return;
8009  }
8010  mCachedMarginValid = false;
8011 
8012  // interpret first char as number format char:
8013  QString allowedFormatChars(QLatin1String("eEfgG"));
8014  if (allowedFormatChars.contains(formatCode.at(0)))
8015  {
8016  mNumberFormatChar = QLatin1Char(formatCode.at(0).toLatin1());
8017  } else
8018  {
8019  qDebug() << Q_FUNC_INFO << "Invalid number format code (first char not in 'eEfgG'):" << formatCode;
8020  return;
8021  }
8022  if (formatCode.length() < 2)
8023  {
8024  mNumberBeautifulPowers = false;
8026  return;
8027  }
8028 
8029  // interpret second char as indicator for beautiful decimal powers:
8030  if (formatCode.at(1) == QLatin1Char('b') && (mNumberFormatChar == QLatin1Char('e') || mNumberFormatChar == QLatin1Char('g')))
8031  {
8032  mNumberBeautifulPowers = true;
8033  } else
8034  {
8035  qDebug() << Q_FUNC_INFO << "Invalid number format code (second char not 'b' or first char neither 'e' nor 'g'):" << formatCode;
8036  return;
8037  }
8038  if (formatCode.length() < 3)
8039  {
8041  return;
8042  }
8043 
8044  // interpret third char as indicator for dot or cross multiplication symbol:
8045  if (formatCode.at(2) == QLatin1Char('c'))
8046  {
8048  } else if (formatCode.at(2) == QLatin1Char('d'))
8049  {
8051  } else
8052  {
8053  qDebug() << Q_FUNC_INFO << "Invalid number format code (third char neither 'c' nor 'd'):" << formatCode;
8054  return;
8055  }
8056 }
8057 
8063 void QCPAxis::setNumberPrecision(int precision)
8064 {
8065  if (mNumberPrecision != precision)
8066  {
8067  mNumberPrecision = precision;
8068  mCachedMarginValid = false;
8069  }
8070 }
8071 
8080 void QCPAxis::setTickLength(int inside, int outside)
8081 {
8082  setTickLengthIn(inside);
8083  setTickLengthOut(outside);
8084 }
8085 
8093 {
8094  if (mAxisPainter->tickLengthIn != inside)
8095  {
8096  mAxisPainter->tickLengthIn = inside;
8097  }
8098 }
8099 
8107 void QCPAxis::setTickLengthOut(int outside)
8108 {
8109  if (mAxisPainter->tickLengthOut != outside)
8110  {
8111  mAxisPainter->tickLengthOut = outside;
8112  mCachedMarginValid = false; // only outside tick length can change margin
8113  }
8114 }
8115 
8123 void QCPAxis::setSubTicks(bool show)
8124 {
8125  if (mSubTicks != show)
8126  {
8127  mSubTicks = show;
8128  mCachedMarginValid = false;
8129  }
8130 }
8131 
8140 void QCPAxis::setSubTickLength(int inside, int outside)
8141 {
8142  setSubTickLengthIn(inside);
8143  setSubTickLengthOut(outside);
8144 }
8145 
8153 {
8154  if (mAxisPainter->subTickLengthIn != inside)
8155  {
8156  mAxisPainter->subTickLengthIn = inside;
8157  }
8158 }
8159 
8168 {
8169  if (mAxisPainter->subTickLengthOut != outside)
8170  {
8171  mAxisPainter->subTickLengthOut = outside;
8172  mCachedMarginValid = false; // only outside tick length can change margin
8173  }
8174 }
8175 
8181 void QCPAxis::setBasePen(const QPen &pen)
8182 {
8183  mBasePen = pen;
8184 }
8185 
8191 void QCPAxis::setTickPen(const QPen &pen)
8192 {
8193  mTickPen = pen;
8194 }
8195 
8201 void QCPAxis::setSubTickPen(const QPen &pen)
8202 {
8203  mSubTickPen = pen;
8204 }
8205 
8211 void QCPAxis::setLabelFont(const QFont &font)
8212 {
8213  if (mLabelFont != font)
8214  {
8215  mLabelFont = font;
8216  mCachedMarginValid = false;
8217  }
8218 }
8219 
8225 void QCPAxis::setLabelColor(const QColor &color)
8226 {
8227  mLabelColor = color;
8228 }
8229 
8234 void QCPAxis::setLabel(const QString &str)
8235 {
8236  if (mLabel != str)
8237  {
8238  mLabel = str;
8239  mCachedMarginValid = false;
8240  }
8241 }
8242 
8249 {
8250  if (mAxisPainter->labelPadding != padding)
8251  {
8253  mCachedMarginValid = false;
8254  }
8255 }
8256 
8268 {
8269  if (mPadding != padding)
8270  {
8271  mPadding = padding;
8272  mCachedMarginValid = false;
8273  }
8274 }
8275 
8285 {
8287 }
8288 
8294 void QCPAxis::setSelectedTickLabelFont(const QFont &font)
8295 {
8296  if (font != mSelectedTickLabelFont)
8297  {
8298  mSelectedTickLabelFont = font;
8299  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
8300  }
8301 }
8302 
8308 void QCPAxis::setSelectedLabelFont(const QFont &font)
8309 {
8310  mSelectedLabelFont = font;
8311  // don't set mCachedMarginValid to false here because margin calculation is always done with non-selected fonts
8312 }
8313 
8319 void QCPAxis::setSelectedTickLabelColor(const QColor &color)
8320 {
8321  if (color != mSelectedTickLabelColor)
8322  {
8323  mSelectedTickLabelColor = color;
8324  }
8325 }
8326 
8332 void QCPAxis::setSelectedLabelColor(const QColor &color)
8333 {
8334  mSelectedLabelColor = color;
8335 }
8336 
8342 void QCPAxis::setSelectedBasePen(const QPen &pen)
8343 {
8344  mSelectedBasePen = pen;
8345 }
8346 
8352 void QCPAxis::setSelectedTickPen(const QPen &pen)
8353 {
8354  mSelectedTickPen = pen;
8355 }
8356 
8362 void QCPAxis::setSelectedSubTickPen(const QPen &pen)
8363 {
8364  mSelectedSubTickPen = pen;
8365 }
8366 
8378 {
8379  mAxisPainter->lowerEnding = ending;
8380 }
8381 
8393 {
8394  mAxisPainter->upperEnding = ending;
8395 }
8396 
8404 void QCPAxis::moveRange(double diff)
8405 {
8406  QCPRange oldRange = mRange;
8407  if (mScaleType == stLinear)
8408  {
8409  mRange.lower += diff;
8410  mRange.upper += diff;
8411  } else // mScaleType == stLogarithmic
8412  {
8413  mRange.lower *= diff;
8414  mRange.upper *= diff;
8415  }
8416  emit rangeChanged(mRange);
8417  emit rangeChanged(mRange, oldRange);
8418 }
8419 
8429 void QCPAxis::scaleRange(double factor)
8430 {
8431  scaleRange(factor, range().center());
8432 }
8433 
8443 void QCPAxis::scaleRange(double factor, double center)
8444 {
8445  QCPRange oldRange = mRange;
8446  if (mScaleType == stLinear)
8447  {
8448  QCPRange newRange;
8449  newRange.lower = (mRange.lower-center)*factor + center;
8450  newRange.upper = (mRange.upper-center)*factor + center;
8451  if (QCPRange::validRange(newRange))
8452  mRange = newRange.sanitizedForLinScale();
8453  } else // mScaleType == stLogarithmic
8454  {
8455  if ((mRange.upper < 0 && center < 0) || (mRange.upper > 0 && center > 0)) // make sure center has same sign as range
8456  {
8457  QCPRange newRange;
8458  newRange.lower = qPow(mRange.lower/center, factor)*center;
8459  newRange.upper = qPow(mRange.upper/center, factor)*center;
8460  if (QCPRange::validRange(newRange))
8461  mRange = newRange.sanitizedForLogScale();
8462  } else
8463  qDebug() << Q_FUNC_INFO << "Center of scaling operation doesn't lie in same logarithmic sign domain as range:" << center;
8464  }
8465  emit rangeChanged(mRange);
8466  emit rangeChanged(mRange, oldRange);
8467 }
8468 
8482 void QCPAxis::setScaleRatio(const QCPAxis *otherAxis, double ratio)
8483 {
8484  int otherPixelSize, ownPixelSize;
8485 
8486  if (otherAxis->orientation() == Qt::Horizontal)
8487  otherPixelSize = otherAxis->axisRect()->width();
8488  else
8489  otherPixelSize = otherAxis->axisRect()->height();
8490 
8491  if (orientation() == Qt::Horizontal)
8492  ownPixelSize = axisRect()->width();
8493  else
8494  ownPixelSize = axisRect()->height();
8495 
8496  double newRangeSize = ratio*otherAxis->range().size()*ownPixelSize/(double)otherPixelSize;
8497  setRange(range().center(), newRangeSize, Qt::AlignCenter);
8498 }
8499 
8506 void QCPAxis::rescale(bool onlyVisiblePlottables)
8507 {
8508  QList<QCPAbstractPlottable*> p = plottables();
8509  QCPRange newRange;
8510  bool haveRange = false;
8511  for (int i=0; i<p.size(); ++i)
8512  {
8513  if (!p.at(i)->realVisibility() && onlyVisiblePlottables)
8514  continue;
8515  QCPRange plottableRange;
8516  bool currentFoundRange;
8517  QCP::SignDomain signDomain = QCP::sdBoth;
8518  if (mScaleType == stLogarithmic)
8519  signDomain = (mRange.upper < 0 ? QCP::sdNegative : QCP::sdPositive);
8520  if (p.at(i)->keyAxis() == this)
8521  plottableRange = p.at(i)->getKeyRange(currentFoundRange, signDomain);
8522  else
8523  plottableRange = p.at(i)->getValueRange(currentFoundRange, signDomain);
8524  if (currentFoundRange)
8525  {
8526  if (!haveRange)
8527  newRange = plottableRange;
8528  else
8529  newRange.expand(plottableRange);
8530  haveRange = true;
8531  }
8532  }
8533  if (haveRange)
8534  {
8535  if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
8536  {
8537  double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
8538  if (mScaleType == stLinear)
8539  {
8540  newRange.lower = center-mRange.size()/2.0;
8541  newRange.upper = center+mRange.size()/2.0;
8542  } else // mScaleType == stLogarithmic
8543  {
8544  newRange.lower = center/qSqrt(mRange.upper/mRange.lower);
8545  newRange.upper = center*qSqrt(mRange.upper/mRange.lower);
8546  }
8547  }
8548  setRange(newRange);
8549  }
8550 }
8551 
8555 double QCPAxis::pixelToCoord(double value) const
8556 {
8557  if (orientation() == Qt::Horizontal)
8558  {
8559  if (mScaleType == stLinear)
8560  {
8561  if (!mRangeReversed)
8562  return (value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.lower;
8563  else
8564  return -(value-mAxisRect->left())/(double)mAxisRect->width()*mRange.size()+mRange.upper;
8565  } else // mScaleType == stLogarithmic
8566  {
8567  if (!mRangeReversed)
8568  return qPow(mRange.upper/mRange.lower, (value-mAxisRect->left())/(double)mAxisRect->width())*mRange.lower;
8569  else
8570  return qPow(mRange.upper/mRange.lower, (mAxisRect->left()-value)/(double)mAxisRect->width())*mRange.upper;
8571  }
8572  } else // orientation() == Qt::Vertical
8573  {
8574  if (mScaleType == stLinear)
8575  {
8576  if (!mRangeReversed)
8577  return (mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.lower;
8578  else
8579  return -(mAxisRect->bottom()-value)/(double)mAxisRect->height()*mRange.size()+mRange.upper;
8580  } else // mScaleType == stLogarithmic
8581  {
8582  if (!mRangeReversed)
8583  return qPow(mRange.upper/mRange.lower, (mAxisRect->bottom()-value)/(double)mAxisRect->height())*mRange.lower;
8584  else
8585  return qPow(mRange.upper/mRange.lower, (value-mAxisRect->bottom())/(double)mAxisRect->height())*mRange.upper;
8586  }
8587  }
8588 }
8589 
8593 double QCPAxis::coordToPixel(double value) const
8594 {
8595  if (orientation() == Qt::Horizontal)
8596  {
8597  if (mScaleType == stLinear)
8598  {
8599  if (!mRangeReversed)
8600  return (value-mRange.lower)/mRange.size()*mAxisRect->width()+mAxisRect->left();
8601  else
8602  return (mRange.upper-value)/mRange.size()*mAxisRect->width()+mAxisRect->left();
8603  } else // mScaleType == stLogarithmic
8604  {
8605  if (value >= 0.0 && mRange.upper < 0.0) // invalid value for logarithmic scale, just draw it outside visible range
8606  return !mRangeReversed ? mAxisRect->right()+200 : mAxisRect->left()-200;
8607  else if (value <= 0.0 && mRange.upper >= 0.0) // invalid value for logarithmic scale, just draw it outside visible range
8608  return !mRangeReversed ? mAxisRect->left()-200 : mAxisRect->right()+200;
8609  else
8610  {
8611  if (!mRangeReversed)
8612  return qLn(value/mRange.lower)/qLn(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
8613  else
8614  return qLn(mRange.upper/value)/qLn(mRange.upper/mRange.lower)*mAxisRect->width()+mAxisRect->left();
8615  }
8616  }
8617  } else // orientation() == Qt::Vertical
8618  {
8619  if (mScaleType == stLinear)
8620  {
8621  if (!mRangeReversed)
8622  return mAxisRect->bottom()-(value-mRange.lower)/mRange.size()*mAxisRect->height();
8623  else
8624  return mAxisRect->bottom()-(mRange.upper-value)/mRange.size()*mAxisRect->height();
8625  } else // mScaleType == stLogarithmic
8626  {
8627  if (value >= 0.0 && mRange.upper < 0.0) // invalid value for logarithmic scale, just draw it outside visible range
8628  return !mRangeReversed ? mAxisRect->top()-200 : mAxisRect->bottom()+200;
8629  else if (value <= 0.0 && mRange.upper >= 0.0) // invalid value for logarithmic scale, just draw it outside visible range
8630  return !mRangeReversed ? mAxisRect->bottom()+200 : mAxisRect->top()-200;
8631  else
8632  {
8633  if (!mRangeReversed)
8634  return mAxisRect->bottom()-qLn(value/mRange.lower)/qLn(mRange.upper/mRange.lower)*mAxisRect->height();
8635  else
8636  return mAxisRect->bottom()-qLn(mRange.upper/value)/qLn(mRange.upper/mRange.lower)*mAxisRect->height();
8637  }
8638  }
8639  }
8640 }
8641 
8652 {
8653  if (!mVisible)
8654  return spNone;
8655 
8656  if (mAxisPainter->axisSelectionBox().contains(pos.toPoint()))
8657  return spAxis;
8658  else if (mAxisPainter->tickLabelsSelectionBox().contains(pos.toPoint()))
8659  return spTickLabels;
8660  else if (mAxisPainter->labelSelectionBox().contains(pos.toPoint()))
8661  return spAxisLabel;
8662  else
8663  return spNone;
8664 }
8665 
8666 /* inherits documentation from base class */
8667 double QCPAxis::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
8668 {
8669  if (!mParentPlot) return -1;
8670  SelectablePart part = getPartAt(pos);
8671  if ((onlySelectable && !mSelectableParts.testFlag(part)) || part == spNone)
8672  return -1;
8673 
8674  if (details)
8675  details->setValue(part);
8676  return mParentPlot->selectionTolerance()*0.99;
8677 }
8678 
8686 QList<QCPAbstractPlottable*> QCPAxis::plottables() const
8687 {
8688  QList<QCPAbstractPlottable*> result;
8689  if (!mParentPlot) return result;
8690 
8691  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
8692  {
8693  if (mParentPlot->mPlottables.at(i)->keyAxis() == this ||mParentPlot->mPlottables.at(i)->valueAxis() == this)
8694  result.append(mParentPlot->mPlottables.at(i));
8695  }
8696  return result;
8697 }
8698 
8704 QList<QCPGraph*> QCPAxis::graphs() const
8705 {
8706  QList<QCPGraph*> result;
8707  if (!mParentPlot) return result;
8708 
8709  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
8710  {
8711  if (mParentPlot->mGraphs.at(i)->keyAxis() == this || mParentPlot->mGraphs.at(i)->valueAxis() == this)
8712  result.append(mParentPlot->mGraphs.at(i));
8713  }
8714  return result;
8715 }
8716 
8723 QList<QCPAbstractItem*> QCPAxis::items() const
8724 {
8725  QList<QCPAbstractItem*> result;
8726  if (!mParentPlot) return result;
8727 
8728  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
8729  {
8730  QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
8731  for (int posId=0; posId<positions.size(); ++posId)
8732  {
8733  if (positions.at(posId)->keyAxis() == this || positions.at(posId)->valueAxis() == this)
8734  {
8735  result.append(mParentPlot->mItems.at(itemId));
8736  break;
8737  }
8738  }
8739  }
8740  return result;
8741 }
8742 
8748 {
8749  switch (side)
8750  {
8751  case QCP::msLeft: return atLeft;
8752  case QCP::msRight: return atRight;
8753  case QCP::msTop: return atTop;
8754  case QCP::msBottom: return atBottom;
8755  default: break;
8756  }
8757  qDebug() << Q_FUNC_INFO << "Invalid margin side passed:" << (int)side;
8758  return atLeft;
8759 }
8760 
8765 {
8766  switch (type)
8767  {
8768  case atLeft: return atRight; break;
8769  case atRight: return atLeft; break;
8770  case atBottom: return atTop; break;
8771  case atTop: return atBottom; break;
8772  default: qDebug() << Q_FUNC_INFO << "invalid axis type"; return atLeft; break;
8773  }
8774 }
8775 
8776 /* inherits documentation from base class */
8777 void QCPAxis::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
8778 {
8779  Q_UNUSED(event)
8780  SelectablePart part = details.value<SelectablePart>();
8781  if (mSelectableParts.testFlag(part))
8782  {
8783  SelectableParts selBefore = mSelectedParts;
8784  setSelectedParts(additive ? mSelectedParts^part : part);
8785  if (selectionStateChanged)
8786  *selectionStateChanged = mSelectedParts != selBefore;
8787  }
8788 }
8789 
8790 /* inherits documentation from base class */
8791 void QCPAxis::deselectEvent(bool *selectionStateChanged)
8792 {
8793  SelectableParts selBefore = mSelectedParts;
8795  if (selectionStateChanged)
8796  *selectionStateChanged = mSelectedParts != selBefore;
8797 }
8798 
8814 void QCPAxis::mousePressEvent(QMouseEvent *event, const QVariant &details)
8815 {
8816  Q_UNUSED(details)
8817  if (!mParentPlot->interactions().testFlag(QCP::iRangeDrag) ||
8818  !mAxisRect->rangeDrag().testFlag(orientation()) ||
8819  !mAxisRect->rangeDragAxes(orientation()).contains(this))
8820  {
8821  event->ignore();
8822  return;
8823  }
8824 
8825  if (event->buttons() & Qt::LeftButton)
8826  {
8827  mDragging = true;
8828  // initialize antialiasing backup in case we start dragging:
8830  {
8833  }
8834  // Mouse range dragging interaction:
8835  if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
8837  }
8838 }
8839 
8852 void QCPAxis::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
8853 {
8854  if (mDragging)
8855  {
8856  const double startPixel = orientation() == Qt::Horizontal ? startPos.x() : startPos.y();
8857  const double currentPixel = orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y();
8859  {
8860  const double diff = pixelToCoord(startPixel) - pixelToCoord(currentPixel);
8862  } else if (mScaleType == QCPAxis::stLogarithmic)
8863  {
8864  const double diff = pixelToCoord(startPixel) / pixelToCoord(currentPixel);
8866  }
8867 
8871  }
8872 }
8873 
8886 void QCPAxis::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
8887 {
8888  Q_UNUSED(event)
8889  Q_UNUSED(startPos)
8890  mDragging = false;
8892  {
8895  }
8896 }
8897 
8913 void QCPAxis::wheelEvent(QWheelEvent *event)
8914 {
8915  // Mouse range zooming interaction:
8916  if (!mParentPlot->interactions().testFlag(QCP::iRangeZoom) ||
8917  !mAxisRect->rangeZoom().testFlag(orientation()) ||
8918  !mAxisRect->rangeZoomAxes(orientation()).contains(this))
8919  {
8920  event->ignore();
8921  return;
8922  }
8923 
8924  const double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
8925  const double factor = qPow(mAxisRect->rangeZoomFactor(orientation()), wheelSteps);
8926  scaleRange(factor, pixelToCoord(orientation() == Qt::Horizontal ? event->pos().x() : event->pos().y()));
8927  mParentPlot->replot();
8928 }
8929 
8946 {
8948 }
8949 
8957 {
8958  QVector<double> subTickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
8959  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
8960  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
8961  tickPositions.reserve(mTickVector.size());
8962  tickLabels.reserve(mTickVector.size());
8963  subTickPositions.reserve(mSubTickVector.size());
8964 
8965  if (mTicks)
8966  {
8967  for (int i=0; i<mTickVector.size(); ++i)
8968  {
8969  tickPositions.append(coordToPixel(mTickVector.at(i)));
8970  if (mTickLabels)
8971  tickLabels.append(mTickVectorLabels.at(i));
8972  }
8973 
8974  if (mSubTicks)
8975  {
8976  const int subTickCount = mSubTickVector.size();
8977  for (int i=0; i<subTickCount; ++i)
8978  subTickPositions.append(coordToPixel(mSubTickVector.at(i)));
8979  }
8980  }
8981 
8982  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to draw the axis.
8983  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
8998  mAxisPainter->tickPositions = tickPositions;
9000  mAxisPainter->subTickPositions = subTickPositions;
9001  mAxisPainter->draw(painter);
9002 }
9003 
9013 {
9014  if (!mParentPlot) return;
9015  if ((!mTicks && !mTickLabels && !mGrid->visible()) || mRange.size() <= 0) return;
9016 
9017  QVector<QString> oldLabels = mTickVectorLabels;
9019  mCachedMarginValid &= mTickVectorLabels == oldLabels; // if labels have changed, margin might have changed, too
9020 }
9021 
9028 {
9029  return mSelectedParts.testFlag(spAxis) ? mSelectedBasePen : mBasePen;
9030 }
9031 
9038 {
9039  return mSelectedParts.testFlag(spAxis) ? mSelectedTickPen : mTickPen;
9040 }
9041 
9048 {
9050 }
9051 
9058 {
9060 }
9061 
9068 {
9070 }
9071 
9078 {
9080 }
9081 
9088 {
9090 }
9091 
9107 {
9108  if (!mVisible) // if not visible, directly return 0, don't cache 0 because we can't react to setVisible in QCPAxis
9109  return 0;
9110 
9111  if (mCachedMarginValid)
9112  return mCachedMargin;
9113 
9114  // run through similar steps as QCPAxis::draw, and calculate margin needed to fit axis and its labels
9115  int margin = 0;
9116 
9117  QVector<double> tickPositions; // the final coordToPixel transformed vector passed to QCPAxisPainter
9118  QVector<QString> tickLabels; // the final vector passed to QCPAxisPainter
9119  tickPositions.reserve(mTickVector.size());
9120  tickLabels.reserve(mTickVector.size());
9121 
9122  if (mTicks)
9123  {
9124  for (int i=0; i<mTickVector.size(); ++i)
9125  {
9126  tickPositions.append(coordToPixel(mTickVector.at(i)));
9127  if (mTickLabels)
9128  tickLabels.append(mTickVectorLabels.at(i));
9129  }
9130  }
9131  // transfer all properties of this axis to QCPAxisPainterPrivate which it needs to calculate the size.
9132  // Note that some axis painter properties are already set by direct feed-through with QCPAxis setters
9139  mAxisPainter->tickPositions = tickPositions;
9141  margin += mAxisPainter->size();
9142  margin += mPadding;
9143 
9144  mCachedMargin = margin;
9145  mCachedMarginValid = true;
9146  return margin;
9147 }
9148 
9149 /* inherits documentation from base class */
9151 {
9152  return QCP::iSelectAxes;
9153 }
9154 
9155 
9159 
9177  type(QCPAxis::atLeft),
9178  basePen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
9179  lowerEnding(QCPLineEnding::esNone),
9180  upperEnding(QCPLineEnding::esNone),
9181  labelPadding(0),
9182  tickLabelPadding(0),
9183  tickLabelRotation(0),
9185  substituteExponent(true),
9186  numberMultiplyCross(false),
9187  tickLengthIn(5),
9188  tickLengthOut(0),
9189  subTickLengthIn(2),
9190  subTickLengthOut(0),
9191  tickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
9192  subTickPen(QPen(Qt::black, 0, Qt::SolidLine, Qt::SquareCap)),
9193  offset(0),
9194  abbreviateDecimalPowers(false),
9195  reversedEndings(false),
9196  mParentPlot(parentPlot),
9197  mLabelCache(16) // cache at most 16 (tick) labels
9198 {
9199 }
9200 
9202 {
9203 }
9204 
9213 {
9214  QByteArray newHash = generateLabelParameterHash();
9215  if (newHash != mLabelParameterHash)
9216  {
9217  mLabelCache.clear();
9218  mLabelParameterHash = newHash;
9219  }
9220 
9221  QPoint origin;
9222  switch (type)
9223  {
9224  case QCPAxis::atLeft: origin = axisRect.bottomLeft() +QPoint(-offset, 0); break;
9225  case QCPAxis::atRight: origin = axisRect.bottomRight()+QPoint(+offset, 0); break;
9226  case QCPAxis::atTop: origin = axisRect.topLeft() +QPoint(0, -offset); break;
9227  case QCPAxis::atBottom: origin = axisRect.bottomLeft() +QPoint(0, +offset); break;
9228  }
9229 
9230  double xCor = 0, yCor = 0; // paint system correction, for pixel exact matches (affects baselines and ticks of top/right axes)
9231  switch (type)
9232  {
9233  case QCPAxis::atTop: yCor = -1; break;
9234  case QCPAxis::atRight: xCor = 1; break;
9235  default: break;
9236  }
9237  int margin = 0;
9238  // draw baseline:
9239  QLineF baseLine;
9240  painter->setPen(basePen);
9241  if (QCPAxis::orientation(type) == Qt::Horizontal)
9242  baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(axisRect.width()+xCor, yCor));
9243  else
9244  baseLine.setPoints(origin+QPointF(xCor, yCor), origin+QPointF(xCor, -axisRect.height()+yCor));
9245  if (reversedEndings)
9246  baseLine = QLineF(baseLine.p2(), baseLine.p1()); // won't make a difference for line itself, but for line endings later
9247  painter->drawLine(baseLine);
9248 
9249  // draw ticks:
9250  if (!tickPositions.isEmpty())
9251  {
9252  painter->setPen(tickPen);
9253  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1; // direction of ticks ("inward" is right for left axis and left for right axis)
9254  if (QCPAxis::orientation(type) == Qt::Horizontal)
9255  {
9256  for (int i=0; i<tickPositions.size(); ++i)
9257  painter->drawLine(QLineF(tickPositions.at(i)+xCor, origin.y()-tickLengthOut*tickDir+yCor, tickPositions.at(i)+xCor, origin.y()+tickLengthIn*tickDir+yCor));
9258  } else
9259  {
9260  for (int i=0; i<tickPositions.size(); ++i)
9261  painter->drawLine(QLineF(origin.x()-tickLengthOut*tickDir+xCor, tickPositions.at(i)+yCor, origin.x()+tickLengthIn*tickDir+xCor, tickPositions.at(i)+yCor));
9262  }
9263  }
9264 
9265  // draw subticks:
9266  if (!subTickPositions.isEmpty())
9267  {
9268  painter->setPen(subTickPen);
9269  // direction of ticks ("inward" is right for left axis and left for right axis)
9270  int tickDir = (type == QCPAxis::atBottom || type == QCPAxis::atRight) ? -1 : 1;
9271  if (QCPAxis::orientation(type) == Qt::Horizontal)
9272  {
9273  for (int i=0; i<subTickPositions.size(); ++i)
9274  painter->drawLine(QLineF(subTickPositions.at(i)+xCor, origin.y()-subTickLengthOut*tickDir+yCor, subTickPositions.at(i)+xCor, origin.y()+subTickLengthIn*tickDir+yCor));
9275  } else
9276  {
9277  for (int i=0; i<subTickPositions.size(); ++i)
9278  painter->drawLine(QLineF(origin.x()-subTickLengthOut*tickDir+xCor, subTickPositions.at(i)+yCor, origin.x()+subTickLengthIn*tickDir+xCor, subTickPositions.at(i)+yCor));
9279  }
9280  }
9281  margin += qMax(0, qMax(tickLengthOut, subTickLengthOut));
9282 
9283  // draw axis base endings:
9284  bool antialiasingBackup = painter->antialiasing();
9285  painter->setAntialiasing(true); // always want endings to be antialiased, even if base and ticks themselves aren't
9286  painter->setBrush(QBrush(basePen.color()));
9287  QCPVector2D baseLineVector(baseLine.dx(), baseLine.dy());
9289  lowerEnding.draw(painter, QCPVector2D(baseLine.p1())-baseLineVector.normalized()*lowerEnding.realLength()*(lowerEnding.inverted()?-1:1), -baseLineVector);
9291  upperEnding.draw(painter, QCPVector2D(baseLine.p2())+baseLineVector.normalized()*upperEnding.realLength()*(upperEnding.inverted()?-1:1), baseLineVector);
9292  painter->setAntialiasing(antialiasingBackup);
9293 
9294  // tick labels:
9295  QRect oldClipRect;
9296  if (tickLabelSide == QCPAxis::lsInside) // if using inside labels, clip them to the axis rect
9297  {
9298  oldClipRect = painter->clipRegion().boundingRect();
9299  painter->setClipRect(axisRect);
9300  }
9301  QSize tickLabelsSize(0, 0); // size of largest tick label, for offset calculation of axis label
9302  if (!tickLabels.isEmpty())
9303  {
9305  margin += tickLabelPadding;
9306  painter->setFont(tickLabelFont);
9307  painter->setPen(QPen(tickLabelColor));
9308  const int maxLabelIndex = qMin(tickPositions.size(), tickLabels.size());
9309  int distanceToAxis = margin;
9311  distanceToAxis = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
9312  for (int i=0; i<maxLabelIndex; ++i)
9313  placeTickLabel(painter, tickPositions.at(i), distanceToAxis, tickLabels.at(i), &tickLabelsSize);
9315  margin += (QCPAxis::orientation(type) == Qt::Horizontal) ? tickLabelsSize.height() : tickLabelsSize.width();
9316  }
9318  painter->setClipRect(oldClipRect);
9319 
9320  // axis label:
9321  QRect labelBounds;
9322  if (!label.isEmpty())
9323  {
9324  margin += labelPadding;
9325  painter->setFont(labelFont);
9326  painter->setPen(QPen(labelColor));
9327  labelBounds = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip, label);
9328  if (type == QCPAxis::atLeft)
9329  {
9330  QTransform oldTransform = painter->transform();
9331  painter->translate((origin.x()-margin-labelBounds.height()), origin.y());
9332  painter->rotate(-90);
9333  painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
9334  painter->setTransform(oldTransform);
9335  }
9336  else if (type == QCPAxis::atRight)
9337  {
9338  QTransform oldTransform = painter->transform();
9339  painter->translate((origin.x()+margin+labelBounds.height()), origin.y()-axisRect.height());
9340  painter->rotate(90);
9341  painter->drawText(0, 0, axisRect.height(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
9342  painter->setTransform(oldTransform);
9343  }
9344  else if (type == QCPAxis::atTop)
9345  painter->drawText(origin.x(), origin.y()-margin-labelBounds.height(), axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
9346  else if (type == QCPAxis::atBottom)
9347  painter->drawText(origin.x(), origin.y()+margin, axisRect.width(), labelBounds.height(), Qt::TextDontClip | Qt::AlignCenter, label);
9348  }
9349 
9350  // set selection boxes:
9351  int selectionTolerance = 0;
9352  if (mParentPlot)
9353  selectionTolerance = mParentPlot->selectionTolerance();
9354  else
9355  qDebug() << Q_FUNC_INFO << "mParentPlot is null";
9356  int selAxisOutSize = qMax(qMax(tickLengthOut, subTickLengthOut), selectionTolerance);
9357  int selAxisInSize = selectionTolerance;
9358  int selTickLabelSize;
9359  int selTickLabelOffset;
9361  {
9362  selTickLabelSize = (QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
9363  selTickLabelOffset = qMax(tickLengthOut, subTickLengthOut)+tickLabelPadding;
9364  } else
9365  {
9366  selTickLabelSize = -(QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width());
9367  selTickLabelOffset = -(qMax(tickLengthIn, subTickLengthIn)+tickLabelPadding);
9368  }
9369  int selLabelSize = labelBounds.height();
9370  int selLabelOffset = qMax(tickLengthOut, subTickLengthOut)+(!tickLabels.isEmpty() && tickLabelSide == QCPAxis::lsOutside ? tickLabelPadding+selTickLabelSize : 0)+labelPadding;
9371  if (type == QCPAxis::atLeft)
9372  {
9373  mAxisSelectionBox.setCoords(origin.x()-selAxisOutSize, axisRect.top(), origin.x()+selAxisInSize, axisRect.bottom());
9374  mTickLabelsSelectionBox.setCoords(origin.x()-selTickLabelOffset-selTickLabelSize, axisRect.top(), origin.x()-selTickLabelOffset, axisRect.bottom());
9375  mLabelSelectionBox.setCoords(origin.x()-selLabelOffset-selLabelSize, axisRect.top(), origin.x()-selLabelOffset, axisRect.bottom());
9376  } else if (type == QCPAxis::atRight)
9377  {
9378  mAxisSelectionBox.setCoords(origin.x()-selAxisInSize, axisRect.top(), origin.x()+selAxisOutSize, axisRect.bottom());
9379  mTickLabelsSelectionBox.setCoords(origin.x()+selTickLabelOffset+selTickLabelSize, axisRect.top(), origin.x()+selTickLabelOffset, axisRect.bottom());
9380  mLabelSelectionBox.setCoords(origin.x()+selLabelOffset+selLabelSize, axisRect.top(), origin.x()+selLabelOffset, axisRect.bottom());
9381  } else if (type == QCPAxis::atTop)
9382  {
9383  mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisOutSize, axisRect.right(), origin.y()+selAxisInSize);
9384  mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()-selTickLabelOffset-selTickLabelSize, axisRect.right(), origin.y()-selTickLabelOffset);
9385  mLabelSelectionBox.setCoords(axisRect.left(), origin.y()-selLabelOffset-selLabelSize, axisRect.right(), origin.y()-selLabelOffset);
9386  } else if (type == QCPAxis::atBottom)
9387  {
9388  mAxisSelectionBox.setCoords(axisRect.left(), origin.y()-selAxisInSize, axisRect.right(), origin.y()+selAxisOutSize);
9389  mTickLabelsSelectionBox.setCoords(axisRect.left(), origin.y()+selTickLabelOffset+selTickLabelSize, axisRect.right(), origin.y()+selTickLabelOffset);
9390  mLabelSelectionBox.setCoords(axisRect.left(), origin.y()+selLabelOffset+selLabelSize, axisRect.right(), origin.y()+selLabelOffset);
9391  }
9392  mAxisSelectionBox = mAxisSelectionBox.normalized();
9394  mLabelSelectionBox = mLabelSelectionBox.normalized();
9395  // draw hitboxes for debug purposes:
9396  //painter->setBrush(Qt::NoBrush);
9397  //painter->drawRects(QVector<QRect>() << mAxisSelectionBox << mTickLabelsSelectionBox << mLabelSelectionBox);
9398 }
9399 
9406 {
9407  int result = 0;
9408 
9409  // get length of tick marks pointing outwards:
9410  if (!tickPositions.isEmpty())
9411  result += qMax(0, qMax(tickLengthOut, subTickLengthOut));
9412 
9413  // calculate size of tick labels:
9415  {
9416  QSize tickLabelsSize(0, 0);
9417  if (!tickLabels.isEmpty())
9418  {
9419  for (int i=0; i<tickLabels.size(); ++i)
9420  getMaxTickLabelSize(tickLabelFont, tickLabels.at(i), &tickLabelsSize);
9421  result += QCPAxis::orientation(type) == Qt::Horizontal ? tickLabelsSize.height() : tickLabelsSize.width();
9422  result += tickLabelPadding;
9423  }
9424  }
9425 
9426  // calculate size of axis label (only height needed, because left/right labels are rotated by 90 degrees):
9427  if (!label.isEmpty())
9428  {
9429  QFontMetrics fontMetrics(labelFont);
9430  QRect bounds;
9431  bounds = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter | Qt::AlignVCenter, label);
9432  result += bounds.height() + labelPadding;
9433  }
9434 
9435  return result;
9436 }
9437 
9445 {
9446  mLabelCache.clear();
9447 }
9448 
9457 {
9458  QByteArray result;
9459  result.append(QByteArray::number(mParentPlot->bufferDevicePixelRatio()));
9460  result.append(QByteArray::number(tickLabelRotation));
9461  result.append(QByteArray::number((int)tickLabelSide));
9462  result.append(QByteArray::number((int)substituteExponent));
9463  result.append(QByteArray::number((int)numberMultiplyCross));
9464  result.append(tickLabelColor.name().toLatin1()+QByteArray::number(tickLabelColor.alpha(), 16));
9465  result.append(tickLabelFont.toString().toLatin1());
9466  return result;
9467 }
9468 
9488 void QCPAxisPainterPrivate::placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
9489 {
9490  // warning: if you change anything here, also adapt getMaxTickLabelSize() accordingly!
9491  if (text.isEmpty()) return;
9492  QSize finalSize;
9493  QPointF labelAnchor;
9494  switch (type)
9495  {
9496  case QCPAxis::atLeft: labelAnchor = QPointF(axisRect.left()-distanceToAxis-offset, position); break;
9497  case QCPAxis::atRight: labelAnchor = QPointF(axisRect.right()+distanceToAxis+offset, position); break;
9498  case QCPAxis::atTop: labelAnchor = QPointF(position, axisRect.top()-distanceToAxis-offset); break;
9499  case QCPAxis::atBottom: labelAnchor = QPointF(position, axisRect.bottom()+distanceToAxis+offset); break;
9500  }
9501  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && !painter->modes().testFlag(QCPPainter::pmNoCaching)) // label caching enabled
9502  {
9503  CachedLabel *cachedLabel = mLabelCache.take(text); // attempt to get label from cache
9504  if (!cachedLabel) // no cached label existed, create it
9505  {
9506  cachedLabel = new CachedLabel;
9507  TickLabelData labelData = getTickLabelData(painter->font(), text);
9508  cachedLabel->offset = getTickLabelDrawOffset(labelData)+labelData.rotatedTotalBounds.topLeft();
9509  if (!qFuzzyCompare(1.0, mParentPlot->bufferDevicePixelRatio()))
9510  {
9511  cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size()*mParentPlot->bufferDevicePixelRatio());
9512 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
9513 # ifdef QCP_DEVICEPIXELRATIO_FLOAT
9514  cachedLabel->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatioF());
9515 # else
9516  cachedLabel->pixmap.setDevicePixelRatio(mParentPlot->devicePixelRatio());
9517 # endif
9518 #endif
9519  } else
9520  cachedLabel->pixmap = QPixmap(labelData.rotatedTotalBounds.size());
9521  cachedLabel->pixmap.fill(Qt::transparent);
9522  QCPPainter cachePainter(&cachedLabel->pixmap);
9523  cachePainter.setPen(painter->pen());
9524  drawTickLabel(&cachePainter, -labelData.rotatedTotalBounds.topLeft().x(), -labelData.rotatedTotalBounds.topLeft().y(), labelData);
9525  }
9526  // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
9527  bool labelClippedByBorder = false;
9529  {
9530  if (QCPAxis::orientation(type) == Qt::Horizontal)
9531  labelClippedByBorder = labelAnchor.x()+cachedLabel->offset.x()+cachedLabel->pixmap.width()/mParentPlot->bufferDevicePixelRatio() > viewportRect.right() || labelAnchor.x()+cachedLabel->offset.x() < viewportRect.left();
9532  else
9533  labelClippedByBorder = labelAnchor.y()+cachedLabel->offset.y()+cachedLabel->pixmap.height()/mParentPlot->bufferDevicePixelRatio() > viewportRect.bottom() || labelAnchor.y()+cachedLabel->offset.y() < viewportRect.top();
9534  }
9535  if (!labelClippedByBorder)
9536  {
9537  painter->drawPixmap(labelAnchor+cachedLabel->offset, cachedLabel->pixmap);
9538  finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio();
9539  }
9540  mLabelCache.insert(text, cachedLabel); // return label to cache or insert for the first time if newly created
9541  } else // label caching disabled, draw text directly on surface:
9542  {
9543  TickLabelData labelData = getTickLabelData(painter->font(), text);
9544  QPointF finalPosition = labelAnchor + getTickLabelDrawOffset(labelData);
9545  // if label would be partly clipped by widget border on sides, don't draw it (only for outside tick labels):
9546  bool labelClippedByBorder = false;
9548  {
9549  if (QCPAxis::orientation(type) == Qt::Horizontal)
9550  labelClippedByBorder = finalPosition.x()+(labelData.rotatedTotalBounds.width()+labelData.rotatedTotalBounds.left()) > viewportRect.right() || finalPosition.x()+labelData.rotatedTotalBounds.left() < viewportRect.left();
9551  else
9552  labelClippedByBorder = finalPosition.y()+(labelData.rotatedTotalBounds.height()+labelData.rotatedTotalBounds.top()) > viewportRect.bottom() || finalPosition.y()+labelData.rotatedTotalBounds.top() < viewportRect.top();
9553  }
9554  if (!labelClippedByBorder)
9555  {
9556  drawTickLabel(painter, finalPosition.x(), finalPosition.y(), labelData);
9557  finalSize = labelData.rotatedTotalBounds.size();
9558  }
9559  }
9560 
9561  // expand passed tickLabelsSize if current tick label is larger:
9562  if (finalSize.width() > tickLabelsSize->width())
9563  tickLabelsSize->setWidth(finalSize.width());
9564  if (finalSize.height() > tickLabelsSize->height())
9565  tickLabelsSize->setHeight(finalSize.height());
9566 }
9567 
9577 void QCPAxisPainterPrivate::drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
9578 {
9579  // backup painter settings that we're about to change:
9580  QTransform oldTransform = painter->transform();
9581  QFont oldFont = painter->font();
9582 
9583  // transform painter to position/rotation:
9584  painter->translate(x, y);
9585  if (!qFuzzyIsNull(tickLabelRotation))
9586  painter->rotate(tickLabelRotation);
9587 
9588  // draw text:
9589  if (!labelData.expPart.isEmpty()) // indicator that beautiful powers must be used
9590  {
9591  painter->setFont(labelData.baseFont);
9592  painter->drawText(0, 0, 0, 0, Qt::TextDontClip, labelData.basePart);
9593  if (!labelData.suffixPart.isEmpty())
9594  painter->drawText(labelData.baseBounds.width()+1+labelData.expBounds.width(), 0, 0, 0, Qt::TextDontClip, labelData.suffixPart);
9595  painter->setFont(labelData.expFont);
9596  painter->drawText(labelData.baseBounds.width()+1, 0, labelData.expBounds.width(), labelData.expBounds.height(), Qt::TextDontClip, labelData.expPart);
9597  } else
9598  {
9599  painter->setFont(labelData.baseFont);
9600  painter->drawText(0, 0, labelData.totalBounds.width(), labelData.totalBounds.height(), Qt::TextDontClip | Qt::AlignHCenter, labelData.basePart);
9601  }
9602 
9603  // reset painter settings to what it was before:
9604  painter->setTransform(oldTransform);
9605  painter->setFont(oldFont);
9606 }
9607 
9617 {
9618  TickLabelData result;
9619 
9620  // determine whether beautiful decimal powers should be used
9621  bool useBeautifulPowers = false;
9622  int ePos = -1; // first index of exponent part, text before that will be basePart, text until eLast will be expPart
9623  int eLast = -1; // last index of exponent part, rest of text after this will be suffixPart
9624  if (substituteExponent)
9625  {
9626  ePos = text.indexOf(QLatin1Char('e'));
9627  if (ePos > 0 && text.at(ePos-1).isDigit())
9628  {
9629  eLast = ePos;
9630  while (eLast+1 < text.size() && (text.at(eLast+1) == QLatin1Char('+') || text.at(eLast+1) == QLatin1Char('-') || text.at(eLast+1).isDigit()))
9631  ++eLast;
9632  if (eLast > ePos) // only if also to right of 'e' is a digit/+/- interpret it as beautifiable power
9633  useBeautifulPowers = true;
9634  }
9635  }
9636 
9637  // calculate text bounding rects and do string preparation for beautiful decimal powers:
9638  result.baseFont = font;
9639  if (result.baseFont.pointSizeF() > 0) // might return -1 if specified with setPixelSize, in that case we can't do correction in next line
9640  result.baseFont.setPointSizeF(result.baseFont.pointSizeF()+0.05); // QFontMetrics.boundingRect has a bug for exact point sizes that make the results oscillate due to internal rounding
9641  if (useBeautifulPowers)
9642  {
9643  // split text into parts of number/symbol that will be drawn normally and part that will be drawn as exponent:
9644  result.basePart = text.left(ePos);
9645  result.suffixPart = text.mid(eLast+1); // also drawn normally but after exponent
9646  // in log scaling, we want to turn "1*10^n" into "10^n", else add multiplication sign and decimal base:
9647  if (abbreviateDecimalPowers && result.basePart == QLatin1String("1"))
9648  result.basePart = QLatin1String("10");
9649  else
9650  result.basePart += (numberMultiplyCross ? QString(QChar(215)) : QString(QChar(183))) + QLatin1String("10");
9651  result.expPart = text.mid(ePos+1, eLast-ePos);
9652  // clip "+" and leading zeros off expPart:
9653  while (result.expPart.length() > 2 && result.expPart.at(1) == QLatin1Char('0')) // length > 2 so we leave one zero when numberFormatChar is 'e'
9654  result.expPart.remove(1, 1);
9655  if (!result.expPart.isEmpty() && result.expPart.at(0) == QLatin1Char('+'))
9656  result.expPart.remove(0, 1);
9657  // prepare smaller font for exponent:
9658  result.expFont = font;
9659  if (result.expFont.pointSize() > 0)
9660  result.expFont.setPointSize(result.expFont.pointSize()*0.75);
9661  else
9662  result.expFont.setPixelSize(result.expFont.pixelSize()*0.75);
9663  // calculate bounding rects of base part(s), exponent part and total one:
9664  result.baseBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.basePart);
9665  result.expBounds = QFontMetrics(result.expFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.expPart);
9666  if (!result.suffixPart.isEmpty())
9667  result.suffixBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip, result.suffixPart);
9668  result.totalBounds = result.baseBounds.adjusted(0, 0, result.expBounds.width()+result.suffixBounds.width()+2, 0); // +2 consists of the 1 pixel spacing between base and exponent (see drawTickLabel) and an extra pixel to include AA
9669  } else // useBeautifulPowers == false
9670  {
9671  result.basePart = text;
9672  result.totalBounds = QFontMetrics(result.baseFont).boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignHCenter, result.basePart);
9673  }
9674  result.totalBounds.moveTopLeft(QPoint(0, 0)); // want bounding box aligned top left at origin, independent of how it was created, to make further processing simpler
9675 
9676  // calculate possibly different bounding rect after rotation:
9677  result.rotatedTotalBounds = result.totalBounds;
9678  if (!qFuzzyIsNull(tickLabelRotation))
9679  {
9680  QTransform transform;
9681  transform.rotate(tickLabelRotation);
9682  result.rotatedTotalBounds = transform.mapRect(result.rotatedTotalBounds);
9683  }
9684 
9685  return result;
9686 }
9687 
9699 {
9700  /*
9701  calculate label offset from base point at tick (non-trivial, for best visual appearance): short
9702  explanation for bottom axis: The anchor, i.e. the point in the label that is placed
9703  horizontally under the corresponding tick is always on the label side that is closer to the
9704  axis (e.g. the left side of the text when we're rotating clockwise). On that side, the height
9705  is halved and the resulting point is defined the anchor. This way, a 90 degree rotated text
9706  will be centered under the tick (i.e. displaced horizontally by half its height). At the same
9707  time, a 45 degree rotated text will "point toward" its tick, as is typical for rotated tick
9708  labels.
9709  */
9710  bool doRotation = !qFuzzyIsNull(tickLabelRotation);
9711  bool flip = qFuzzyCompare(qAbs(tickLabelRotation), 90.0); // perfect +/-90 degree flip. Indicates vertical label centering on vertical axes.
9712  double radians = tickLabelRotation/180.0*M_PI;
9713  int x=0, y=0;
9714  if ((type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsInside)) // Anchor at right side of tick label
9715  {
9716  if (doRotation)
9717  {
9718  if (tickLabelRotation > 0)
9719  {
9720  x = -qCos(radians)*labelData.totalBounds.width();
9721  y = flip ? -labelData.totalBounds.width()/2.0 : -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height()/2.0;
9722  } else
9723  {
9724  x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height();
9725  y = flip ? +labelData.totalBounds.width()/2.0 : +qSin(-radians)*labelData.totalBounds.width()-qCos(-radians)*labelData.totalBounds.height()/2.0;
9726  }
9727  } else
9728  {
9729  x = -labelData.totalBounds.width();
9730  y = -labelData.totalBounds.height()/2.0;
9731  }
9732  } else if ((type == QCPAxis::atRight && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atLeft && tickLabelSide == QCPAxis::lsInside)) // Anchor at left side of tick label
9733  {
9734  if (doRotation)
9735  {
9736  if (tickLabelRotation > 0)
9737  {
9738  x = +qSin(radians)*labelData.totalBounds.height();
9739  y = flip ? -labelData.totalBounds.width()/2.0 : -qCos(radians)*labelData.totalBounds.height()/2.0;
9740  } else
9741  {
9742  x = 0;
9743  y = flip ? +labelData.totalBounds.width()/2.0 : -qCos(-radians)*labelData.totalBounds.height()/2.0;
9744  }
9745  } else
9746  {
9747  x = 0;
9748  y = -labelData.totalBounds.height()/2.0;
9749  }
9750  } else if ((type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsInside)) // Anchor at bottom side of tick label
9751  {
9752  if (doRotation)
9753  {
9754  if (tickLabelRotation > 0)
9755  {
9756  x = -qCos(radians)*labelData.totalBounds.width()+qSin(radians)*labelData.totalBounds.height()/2.0;
9757  y = -qSin(radians)*labelData.totalBounds.width()-qCos(radians)*labelData.totalBounds.height();
9758  } else
9759  {
9760  x = -qSin(-radians)*labelData.totalBounds.height()/2.0;
9761  y = -qCos(-radians)*labelData.totalBounds.height();
9762  }
9763  } else
9764  {
9765  x = -labelData.totalBounds.width()/2.0;
9766  y = -labelData.totalBounds.height();
9767  }
9768  } else if ((type == QCPAxis::atBottom && tickLabelSide == QCPAxis::lsOutside) || (type == QCPAxis::atTop && tickLabelSide == QCPAxis::lsInside)) // Anchor at top side of tick label
9769  {
9770  if (doRotation)
9771  {
9772  if (tickLabelRotation > 0)
9773  {
9774  x = +qSin(radians)*labelData.totalBounds.height()/2.0;
9775  y = 0;
9776  } else
9777  {
9778  x = -qCos(-radians)*labelData.totalBounds.width()-qSin(-radians)*labelData.totalBounds.height()/2.0;
9779  y = +qSin(-radians)*labelData.totalBounds.width();
9780  }
9781  } else
9782  {
9783  x = -labelData.totalBounds.width()/2.0;
9784  y = 0;
9785  }
9786  }
9787 
9788  return QPointF(x, y);
9789 }
9790 
9798 void QCPAxisPainterPrivate::getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
9799 {
9800  // note: this function must return the same tick label sizes as the placeTickLabel function.
9801  QSize finalSize;
9802  if (mParentPlot->plottingHints().testFlag(QCP::phCacheLabels) && mLabelCache.contains(text)) // label caching enabled and have cached label
9803  {
9804  const CachedLabel *cachedLabel = mLabelCache.object(text);
9805  finalSize = cachedLabel->pixmap.size()/mParentPlot->bufferDevicePixelRatio();
9806  } else // label caching disabled or no label with this text cached:
9807  {
9808  TickLabelData labelData = getTickLabelData(font, text);
9809  finalSize = labelData.rotatedTotalBounds.size();
9810  }
9811 
9812  // expand passed tickLabelsSize if current tick label is larger:
9813  if (finalSize.width() > tickLabelsSize->width())
9814  tickLabelsSize->setWidth(finalSize.width());
9815  if (finalSize.height() > tickLabelsSize->height())
9816  tickLabelsSize->setHeight(finalSize.height());
9817 }
9818 /* end of 'src/axis/axis.cpp' */
9819 
9820 
9821 /* including file 'src/scatterstyle.cpp', size 17450 */
9822 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
9823 
9827 
9878 /* start documentation of inline functions */
9879 
9901 /* end documentation of inline functions */
9902 
9910  mSize(6),
9911  mShape(ssNone),
9912  mPen(Qt::NoPen),
9913  mBrush(Qt::NoBrush),
9914  mPenDefined(false)
9915 {
9916 }
9917 
9926  mSize(size),
9927  mShape(shape),
9928  mPen(Qt::NoPen),
9929  mBrush(Qt::NoBrush),
9930  mPenDefined(false)
9931 {
9932 }
9933 
9939  mSize(size),
9940  mShape(shape),
9941  mPen(QPen(color)),
9942  mBrush(Qt::NoBrush),
9943  mPenDefined(true)
9944 {
9945 }
9946 
9951 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QColor &color, const QColor &fill, double size) :
9952  mSize(size),
9953  mShape(shape),
9954  mPen(QPen(color)),
9955  mBrush(QBrush(fill)),
9956  mPenDefined(true)
9957 {
9958 }
9959 
9975 QCPScatterStyle::QCPScatterStyle(ScatterShape shape, const QPen &pen, const QBrush &brush, double size) :
9976  mSize(size),
9977  mShape(shape),
9978  mPen(pen),
9979  mBrush(brush),
9980  mPenDefined(pen.style() != Qt::NoPen)
9981 {
9982 }
9983 
9989  mSize(5),
9990  mShape(ssPixmap),
9991  mPen(Qt::NoPen),
9992  mBrush(Qt::NoBrush),
9993  mPixmap(pixmap),
9994  mPenDefined(false)
9995 {
9996 }
9997 
10007 QCPScatterStyle::QCPScatterStyle(const QPainterPath &customPath, const QPen &pen, const QBrush &brush, double size) :
10008  mSize(size),
10009  mShape(ssCustom),
10010  mPen(pen),
10011  mBrush(brush),
10012  mCustomPath(customPath),
10013  mPenDefined(pen.style() != Qt::NoPen)
10014 {
10015 }
10016 
10020 void QCPScatterStyle::setFromOther(const QCPScatterStyle &other, ScatterProperties properties)
10021 {
10022  if (properties.testFlag(spPen))
10023  {
10024  setPen(other.pen());
10025  if (!other.isPenDefined())
10026  undefinePen();
10027  }
10028  if (properties.testFlag(spBrush))
10029  setBrush(other.brush());
10030  if (properties.testFlag(spSize))
10031  setSize(other.size());
10032  if (properties.testFlag(spShape))
10033  {
10034  setShape(other.shape());
10035  if (other.shape() == ssPixmap)
10036  setPixmap(other.pixmap());
10037  else if (other.shape() == ssCustom)
10038  setCustomPath(other.customPath());
10039  }
10040 }
10041 
10048 {
10049  mSize = size;
10050 }
10051 
10061 {
10062  mShape = shape;
10063 }
10064 
10074 void QCPScatterStyle::setPen(const QPen &pen)
10075 {
10076  mPenDefined = true;
10077  mPen = pen;
10078 }
10079 
10087 {
10088  mBrush = brush;
10089 }
10090 
10099 {
10100  setShape(ssPixmap);
10101  mPixmap = pixmap;
10102 }
10103 
10110 {
10111  setShape(ssCustom);
10113 }
10114 
10122 {
10123  mPenDefined = false;
10124 }
10125 
10135 void QCPScatterStyle::applyTo(QCPPainter *painter, const QPen &defaultPen) const
10136 {
10137  painter->setPen(mPenDefined ? mPen : defaultPen);
10138  painter->setBrush(mBrush);
10139 }
10140 
10149 void QCPScatterStyle::drawShape(QCPPainter *painter, const QPointF &pos) const
10150 {
10151  drawShape(painter, pos.x(), pos.y());
10152 }
10153 
10157 void QCPScatterStyle::drawShape(QCPPainter *painter, double x, double y) const
10158 {
10159  double w = mSize/2.0;
10160  switch (mShape)
10161  {
10162  case ssNone: break;
10163  case ssDot:
10164  {
10165  painter->drawLine(QPointF(x, y), QPointF(x+0.0001, y));
10166  break;
10167  }
10168  case ssCross:
10169  {
10170  painter->drawLine(QLineF(x-w, y-w, x+w, y+w));
10171  painter->drawLine(QLineF(x-w, y+w, x+w, y-w));
10172  break;
10173  }
10174  case ssPlus:
10175  {
10176  painter->drawLine(QLineF(x-w, y, x+w, y));
10177  painter->drawLine(QLineF( x, y+w, x, y-w));
10178  break;
10179  }
10180  case ssCircle:
10181  {
10182  painter->drawEllipse(QPointF(x , y), w, w);
10183  break;
10184  }
10185  case ssDisc:
10186  {
10187  QBrush b = painter->brush();
10188  painter->setBrush(painter->pen().color());
10189  painter->drawEllipse(QPointF(x , y), w, w);
10190  painter->setBrush(b);
10191  break;
10192  }
10193  case ssSquare:
10194  {
10195  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
10196  break;
10197  }
10198  case ssDiamond:
10199  {
10200  QPointF lineArray[4] = {QPointF(x-w, y),
10201  QPointF( x, y-w),
10202  QPointF(x+w, y),
10203  QPointF( x, y+w)};
10204  painter->drawPolygon(lineArray, 4);
10205  break;
10206  }
10207  case ssStar:
10208  {
10209  painter->drawLine(QLineF(x-w, y, x+w, y));
10210  painter->drawLine(QLineF( x, y+w, x, y-w));
10211  painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.707, y+w*0.707));
10212  painter->drawLine(QLineF(x-w*0.707, y+w*0.707, x+w*0.707, y-w*0.707));
10213  break;
10214  }
10215  case ssTriangle:
10216  {
10217  QPointF lineArray[3] = {QPointF(x-w, y+0.755*w),
10218  QPointF(x+w, y+0.755*w),
10219  QPointF( x, y-0.977*w)};
10220  painter->drawPolygon(lineArray, 3);
10221  break;
10222  }
10223  case ssTriangleInverted:
10224  {
10225  QPointF lineArray[3] = {QPointF(x-w, y-0.755*w),
10226  QPointF(x+w, y-0.755*w),
10227  QPointF( x, y+0.977*w)};
10228  painter->drawPolygon(lineArray, 3);
10229  break;
10230  }
10231  case ssCrossSquare:
10232  {
10233  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
10234  painter->drawLine(QLineF(x-w, y-w, x+w*0.95, y+w*0.95));
10235  painter->drawLine(QLineF(x-w, y+w*0.95, x+w*0.95, y-w));
10236  break;
10237  }
10238  case ssPlusSquare:
10239  {
10240  painter->drawRect(QRectF(x-w, y-w, mSize, mSize));
10241  painter->drawLine(QLineF(x-w, y, x+w*0.95, y));
10242  painter->drawLine(QLineF( x, y+w, x, y-w));
10243  break;
10244  }
10245  case ssCrossCircle:
10246  {
10247  painter->drawEllipse(QPointF(x, y), w, w);
10248  painter->drawLine(QLineF(x-w*0.707, y-w*0.707, x+w*0.670, y+w*0.670));
10249  painter->drawLine(QLineF(x-w*0.707, y+w*0.670, x+w*0.670, y-w*0.707));
10250  break;
10251  }
10252  case ssPlusCircle:
10253  {
10254  painter->drawEllipse(QPointF(x, y), w, w);
10255  painter->drawLine(QLineF(x-w, y, x+w, y));
10256  painter->drawLine(QLineF( x, y+w, x, y-w));
10257  break;
10258  }
10259  case ssPeace:
10260  {
10261  painter->drawEllipse(QPointF(x, y), w, w);
10262  painter->drawLine(QLineF(x, y-w, x, y+w));
10263  painter->drawLine(QLineF(x, y, x-w*0.707, y+w*0.707));
10264  painter->drawLine(QLineF(x, y, x+w*0.707, y+w*0.707));
10265  break;
10266  }
10267  case ssPixmap:
10268  {
10269  const double widthHalf = mPixmap.width()*0.5;
10270  const double heightHalf = mPixmap.height()*0.5;
10271 #if QT_VERSION < QT_VERSION_CHECK(4, 8, 0)
10272  const QRectF clipRect = painter->clipRegion().boundingRect().adjusted(-widthHalf, -heightHalf, widthHalf, heightHalf);
10273 #else
10274  const QRectF clipRect = painter->clipBoundingRect().adjusted(-widthHalf, -heightHalf, widthHalf, heightHalf);
10275 #endif
10276  if (clipRect.contains(x, y))
10277  painter->drawPixmap(x-widthHalf, y-heightHalf, mPixmap);
10278  break;
10279  }
10280  case ssCustom:
10281  {
10282  QTransform oldTransform = painter->transform();
10283  painter->translate(x, y);
10284  painter->scale(mSize/6.0, mSize/6.0);
10285  painter->drawPath(mCustomPath);
10286  painter->setTransform(oldTransform);
10287  break;
10288  }
10289  }
10290 }
10291 /* end of 'src/scatterstyle.cpp' */
10292 
10293 //amalgamation: add datacontainer.cpp
10294 
10295 /* including file 'src/plottable.cpp', size 38845 */
10296 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
10297 
10301 
10332  mPen(QColor(80, 80, 255), 2.5),
10333  mBrush(Qt::NoBrush),
10334  mScatterStyle(),
10335  mUsedScatterProperties(QCPScatterStyle::spNone),
10336  mPlottable(0)
10337 {
10338 }
10339 
10341 {
10342 }
10343 
10348 {
10349  mPen = pen;
10350 }
10351 
10356 {
10357  mBrush = brush;
10358 }
10359 
10367 void QCPSelectionDecorator::setScatterStyle(const QCPScatterStyle &scatterStyle, QCPScatterStyle::ScatterProperties usedProperties)
10368 {
10370  setUsedScatterProperties(usedProperties);
10371 }
10372 
10380 void QCPSelectionDecorator::setUsedScatterProperties(const QCPScatterStyle::ScatterProperties &properties)
10381 {
10382  mUsedScatterProperties = properties;
10383 }
10384 
10391 {
10392  painter->setPen(mPen);
10393 }
10394 
10401 {
10402  painter->setBrush(mBrush);
10403 }
10404 
10414 {
10415  QCPScatterStyle result(unselectedStyle);
10417 
10418  // if style shall inherit pen from plottable (has no own pen defined), give it the selected
10419  // plottable pen explicitly, so it doesn't use the unselected plottable pen when used in the
10420  // plottable:
10421  if (!result.isPenDefined())
10422  result.setPen(mPen);
10423 
10424  return result;
10425 }
10426 
10432 {
10433  setPen(other->pen());
10434  setBrush(other->brush());
10436 }
10437 
10447 {
10448  Q_UNUSED(painter)
10449  Q_UNUSED(selection)
10450 }
10451 
10462 {
10463  if (!mPlottable)
10464  {
10465  mPlottable = plottable;
10466  return true;
10467  } else
10468  {
10469  qDebug() << Q_FUNC_INFO << "This selection decorator is already registered with plottable:" << reinterpret_cast<quintptr>(mPlottable);
10470  return false;
10471  }
10472 }
10473 
10474 
10478 
10560 /* start of documentation of inline functions */
10561 
10596 /* end of documentation of inline functions */
10597 /* start of documentation of pure virtual functions */
10598 
10649 /* end of documentation of pure virtual functions */
10650 /* start of documentation of signals */
10651 
10677 /* end of documentation of signals */
10678 
10691  QCPLayerable(keyAxis->parentPlot(), QString(), keyAxis->axisRect()),
10692  mName(),
10693  mAntialiasedFill(true),
10694  mAntialiasedScatters(true),
10695  mPen(Qt::black),
10696  mBrush(Qt::NoBrush),
10697  mKeyAxis(keyAxis),
10698  mValueAxis(valueAxis),
10699  mSelectable(QCP::stWhole),
10700  mSelectionDecorator(0)
10701 {
10702  if (keyAxis->parentPlot() != valueAxis->parentPlot())
10703  qDebug() << Q_FUNC_INFO << "Parent plot of keyAxis is not the same as that of valueAxis.";
10704  if (keyAxis->orientation() == valueAxis->orientation())
10705  qDebug() << Q_FUNC_INFO << "keyAxis and valueAxis must be orthogonal to each other.";
10706 
10709 }
10710 
10712 {
10713  if (mSelectionDecorator)
10714  {
10715  delete mSelectionDecorator;
10716  mSelectionDecorator = 0;
10717  }
10718 }
10719 
10725 {
10726  mName = name;
10727 }
10728 
10736 {
10737  mAntialiasedFill = enabled;
10738 }
10739 
10747 {
10748  mAntialiasedScatters = enabled;
10749 }
10750 
10760 {
10761  mPen = pen;
10762 }
10763 
10774 {
10775  mBrush = brush;
10776 }
10777 
10790 {
10791  mKeyAxis = axis;
10792 }
10793 
10806 {
10807  mValueAxis = axis;
10808 }
10809 
10810 
10830 {
10831  selection.enforceType(mSelectable);
10832  if (mSelection != selection)
10833  {
10835  emit selectionChanged(selected());
10837  }
10838 }
10839 
10850 {
10851  if (decorator)
10852  {
10853  if (decorator->registerWithPlottable(this))
10854  {
10855  if (mSelectionDecorator) // delete old decorator if necessary
10856  delete mSelectionDecorator;
10857  mSelectionDecorator = decorator;
10858  }
10859  } else if (mSelectionDecorator) // just clear decorator
10860  {
10861  delete mSelectionDecorator;
10862  mSelectionDecorator = 0;
10863  }
10864 }
10865 
10877 {
10878  if (mSelectable != selectable)
10879  {
10881  QCPDataSelection oldSelection = mSelection;
10884  if (mSelection != oldSelection)
10885  {
10886  emit selectionChanged(selected());
10888  }
10889  }
10890 }
10891 
10892 
10902 void QCPAbstractPlottable::coordsToPixels(double key, double value, double &x, double &y) const
10903 {
10904  QCPAxis *keyAxis = mKeyAxis.data();
10905  QCPAxis *valueAxis = mValueAxis.data();
10906  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
10907 
10908  if (keyAxis->orientation() == Qt::Horizontal)
10909  {
10910  x = keyAxis->coordToPixel(key);
10911  y = valueAxis->coordToPixel(value);
10912  } else
10913  {
10914  y = keyAxis->coordToPixel(key);
10915  x = valueAxis->coordToPixel(value);
10916  }
10917 }
10918 
10923 const QPointF QCPAbstractPlottable::coordsToPixels(double key, double value) const
10924 {
10925  QCPAxis *keyAxis = mKeyAxis.data();
10926  QCPAxis *valueAxis = mValueAxis.data();
10927  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
10928 
10929  if (keyAxis->orientation() == Qt::Horizontal)
10930  return QPointF(keyAxis->coordToPixel(key), valueAxis->coordToPixel(value));
10931  else
10932  return QPointF(valueAxis->coordToPixel(value), keyAxis->coordToPixel(key));
10933 }
10934 
10944 void QCPAbstractPlottable::pixelsToCoords(double x, double y, double &key, double &value) const
10945 {
10946  QCPAxis *keyAxis = mKeyAxis.data();
10947  QCPAxis *valueAxis = mValueAxis.data();
10948  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
10949 
10950  if (keyAxis->orientation() == Qt::Horizontal)
10951  {
10952  key = keyAxis->pixelToCoord(x);
10953  value = valueAxis->pixelToCoord(y);
10954  } else
10955  {
10956  key = keyAxis->pixelToCoord(y);
10957  value = valueAxis->pixelToCoord(x);
10958  }
10959 }
10960 
10965 void QCPAbstractPlottable::pixelsToCoords(const QPointF &pixelPos, double &key, double &value) const
10966 {
10967  pixelsToCoords(pixelPos.x(), pixelPos.y(), key, value);
10968 }
10969 
10983 void QCPAbstractPlottable::rescaleAxes(bool onlyEnlarge) const
10984 {
10985  rescaleKeyAxis(onlyEnlarge);
10986  rescaleValueAxis(onlyEnlarge);
10987 }
10988 
10994 void QCPAbstractPlottable::rescaleKeyAxis(bool onlyEnlarge) const
10995 {
10996  QCPAxis *keyAxis = mKeyAxis.data();
10997  if (!keyAxis) { qDebug() << Q_FUNC_INFO << "invalid key axis"; return; }
10998 
10999  QCP::SignDomain signDomain = QCP::sdBoth;
11000  if (keyAxis->scaleType() == QCPAxis::stLogarithmic)
11001  signDomain = (keyAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive);
11002 
11003  bool foundRange;
11004  QCPRange newRange = getKeyRange(foundRange, signDomain);
11005  if (foundRange)
11006  {
11007  if (onlyEnlarge)
11008  newRange.expand(keyAxis->range());
11009  if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
11010  {
11011  double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
11012  if (keyAxis->scaleType() == QCPAxis::stLinear)
11013  {
11014  newRange.lower = center-keyAxis->range().size()/2.0;
11015  newRange.upper = center+keyAxis->range().size()/2.0;
11016  } else // scaleType() == stLogarithmic
11017  {
11018  newRange.lower = center/qSqrt(keyAxis->range().upper/keyAxis->range().lower);
11019  newRange.upper = center*qSqrt(keyAxis->range().upper/keyAxis->range().lower);
11020  }
11021  }
11022  keyAxis->setRange(newRange);
11023  }
11024 }
11025 
11036 void QCPAbstractPlottable::rescaleValueAxis(bool onlyEnlarge, bool inKeyRange) const
11037 {
11038  QCPAxis *keyAxis = mKeyAxis.data();
11039  QCPAxis *valueAxis = mValueAxis.data();
11040  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
11041 
11042  QCP::SignDomain signDomain = QCP::sdBoth;
11043  if (valueAxis->scaleType() == QCPAxis::stLogarithmic)
11044  signDomain = (valueAxis->range().upper < 0 ? QCP::sdNegative : QCP::sdPositive);
11045 
11046  bool foundRange;
11047  QCPRange newRange = getValueRange(foundRange, signDomain, inKeyRange ? keyAxis->range() : QCPRange());
11048  if (foundRange)
11049  {
11050  if (onlyEnlarge)
11051  newRange.expand(valueAxis->range());
11052  if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this axis dimension), shift current range to at least center the plottable
11053  {
11054  double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
11055  if (valueAxis->scaleType() == QCPAxis::stLinear)
11056  {
11057  newRange.lower = center-valueAxis->range().size()/2.0;
11058  newRange.upper = center+valueAxis->range().size()/2.0;
11059  } else // scaleType() == stLogarithmic
11060  {
11061  newRange.lower = center/qSqrt(valueAxis->range().upper/valueAxis->range().lower);
11062  newRange.upper = center*qSqrt(valueAxis->range().upper/valueAxis->range().lower);
11063  }
11064  }
11065  valueAxis->setRange(newRange);
11066  }
11067 }
11068 
11084 {
11085  if (!legend)
11086  {
11087  qDebug() << Q_FUNC_INFO << "passed legend is null";
11088  return false;
11089  }
11090  if (legend->parentPlot() != mParentPlot)
11091  {
11092  qDebug() << Q_FUNC_INFO << "passed legend isn't in the same QCustomPlot as this plottable";
11093  return false;
11094  }
11095 
11096  if (!legend->hasItemWithPlottable(this))
11097  {
11098  legend->addItem(new QCPPlottableLegendItem(legend, this));
11099  return true;
11100  } else
11101  return false;
11102 }
11103 
11111 {
11112  if (!mParentPlot || !mParentPlot->legend)
11113  return false;
11114  else
11115  return addToLegend(mParentPlot->legend);
11116 }
11117 
11129 {
11130  if (!legend)
11131  {
11132  qDebug() << Q_FUNC_INFO << "passed legend is null";
11133  return false;
11134  }
11135 
11136  if (QCPPlottableLegendItem *lip = legend->itemWithPlottable(this))
11137  return legend->removeItem(lip);
11138  else
11139  return false;
11140 }
11141 
11149 {
11150  if (!mParentPlot || !mParentPlot->legend)
11151  return false;
11152  else
11154 }
11155 
11156 /* inherits documentation from base class */
11158 {
11159  if (mKeyAxis && mValueAxis)
11160  return mKeyAxis.data()->axisRect()->rect() & mValueAxis.data()->axisRect()->rect();
11161  else
11162  return QRect();
11163 }
11164 
11165 /* inherits documentation from base class */
11167 {
11168  return QCP::iSelectPlottables;
11169 }
11170 
11187 {
11189 }
11190 
11203 {
11205 }
11206 
11219 {
11221 }
11222 
11223 /* inherits documentation from base class */
11224 void QCPAbstractPlottable::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
11225 {
11226  Q_UNUSED(event)
11227 
11228  if (mSelectable != QCP::stNone)
11229  {
11230  QCPDataSelection newSelection = details.value<QCPDataSelection>();
11231  QCPDataSelection selectionBefore = mSelection;
11232  if (additive)
11233  {
11234  if (mSelectable == QCP::stWhole) // in whole selection mode, we toggle to no selection even if currently unselected point was hit
11235  {
11236  if (selected())
11238  else
11239  setSelection(newSelection);
11240  } else // in all other selection modes we toggle selections of homogeneously selected/unselected segments
11241  {
11242  if (mSelection.contains(newSelection)) // if entire newSelection is already selected, toggle selection
11243  setSelection(mSelection-newSelection);
11244  else
11245  setSelection(mSelection+newSelection);
11246  }
11247  } else
11248  setSelection(newSelection);
11249  if (selectionStateChanged)
11250  *selectionStateChanged = mSelection != selectionBefore;
11251  }
11252 }
11253 
11254 /* inherits documentation from base class */
11255 void QCPAbstractPlottable::deselectEvent(bool *selectionStateChanged)
11256 {
11257  if (mSelectable != QCP::stNone)
11258  {
11259  QCPDataSelection selectionBefore = mSelection;
11261  if (selectionStateChanged)
11262  *selectionStateChanged = mSelection != selectionBefore;
11263  }
11264 }
11265 /* end of 'src/plottable.cpp' */
11266 
11267 
11268 /* including file 'src/item.cpp', size 49269 */
11269 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
11270 
11274 
11296 /* start documentation of inline functions */
11297 
11308 /* end documentation of inline functions */
11309 
11315 QCPItemAnchor::QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name, int anchorId) :
11316  mName(name),
11317  mParentPlot(parentPlot),
11318  mParentItem(parentItem),
11319  mAnchorId(anchorId)
11320 {
11321 }
11322 
11324 {
11325  // unregister as parent at children:
11326  foreach (QCPItemPosition *child, mChildrenX.toList())
11327  {
11328  if (child->parentAnchorX() == this)
11329  child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
11330  }
11331  foreach (QCPItemPosition *child, mChildrenY.toList())
11332  {
11333  if (child->parentAnchorY() == this)
11334  child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
11335  }
11336 }
11337 
11345 {
11346  if (mParentItem)
11347  {
11348  if (mAnchorId > -1)
11349  {
11351  } else
11352  {
11353  qDebug() << Q_FUNC_INFO << "no valid anchor id set:" << mAnchorId;
11354  return QPointF();
11355  }
11356  } else
11357  {
11358  qDebug() << Q_FUNC_INFO << "no parent item set";
11359  return QPointF();
11360  }
11361 }
11362 
11372 {
11373  if (!mChildrenX.contains(pos))
11374  mChildrenX.insert(pos);
11375  else
11376  qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
11377 }
11378 
11386 {
11387  if (!mChildrenX.remove(pos))
11388  qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
11389 }
11390 
11400 {
11401  if (!mChildrenY.contains(pos))
11402  mChildrenY.insert(pos);
11403  else
11404  qDebug() << Q_FUNC_INFO << "provided pos is child already" << reinterpret_cast<quintptr>(pos);
11405 }
11406 
11414 {
11415  if (!mChildrenY.remove(pos))
11416  qDebug() << Q_FUNC_INFO << "provided pos isn't child" << reinterpret_cast<quintptr>(pos);
11417 }
11418 
11419 
11423 
11458 /* start documentation of inline functions */
11459 
11481 /* end documentation of inline functions */
11482 
11488 QCPItemPosition::QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name) :
11489  QCPItemAnchor(parentPlot, parentItem, name),
11490  mPositionTypeX(ptAbsolute),
11491  mPositionTypeY(ptAbsolute),
11492  mKey(0),
11493  mValue(0),
11494  mParentAnchorX(0),
11495  mParentAnchorY(0)
11496 {
11497 }
11498 
11500 {
11501  // unregister as parent at children:
11502  // Note: this is done in ~QCPItemAnchor again, but it's important QCPItemPosition does it itself, because only then
11503  // the setParentAnchor(0) call the correct QCPItemPosition::pixelPosition function instead of QCPItemAnchor::pixelPosition
11504  foreach (QCPItemPosition *child, mChildrenX.toList())
11505  {
11506  if (child->parentAnchorX() == this)
11507  child->setParentAnchorX(0); // this acts back on this anchor and child removes itself from mChildrenX
11508  }
11509  foreach (QCPItemPosition *child, mChildrenY.toList())
11510  {
11511  if (child->parentAnchorY() == this)
11512  child->setParentAnchorY(0); // this acts back on this anchor and child removes itself from mChildrenY
11513  }
11514  // unregister as child in parent:
11515  if (mParentAnchorX)
11517  if (mParentAnchorY)
11519 }
11520 
11521 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
11523 {
11524  return mAxisRect.data();
11525 }
11526 
11553 {
11554  setTypeX(type);
11555  setTypeY(type);
11556 }
11557 
11566 {
11567  if (mPositionTypeX != type)
11568  {
11569  // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
11570  // were deleted), don't try to recover the pixelPosition() because it would output a qDebug warning.
11571  bool retainPixelPosition = true;
11572  if ((mPositionTypeX == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
11573  retainPixelPosition = false;
11574  if ((mPositionTypeX == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
11575  retainPixelPosition = false;
11576 
11577  QPointF pixel;
11578  if (retainPixelPosition)
11579  pixel = pixelPosition();
11580 
11581  mPositionTypeX = type;
11582 
11583  if (retainPixelPosition)
11584  setPixelPosition(pixel);
11585  }
11586 }
11587 
11596 {
11597  if (mPositionTypeY != type)
11598  {
11599  // if switching from or to coordinate type that isn't valid (e.g. because axes or axis rect
11600  // were deleted), don't try to recover the pixelPosition() because it would output a qDebug warning.
11601  bool retainPixelPosition = true;
11602  if ((mPositionTypeY == ptPlotCoords || type == ptPlotCoords) && (!mKeyAxis || !mValueAxis))
11603  retainPixelPosition = false;
11604  if ((mPositionTypeY == ptAxisRectRatio || type == ptAxisRectRatio) && (!mAxisRect))
11605  retainPixelPosition = false;
11606 
11607  QPointF pixel;
11608  if (retainPixelPosition)
11609  pixel = pixelPosition();
11610 
11611  mPositionTypeY = type;
11612 
11613  if (retainPixelPosition)
11614  setPixelPosition(pixel);
11615  }
11616 }
11617 
11637 {
11638  bool successX = setParentAnchorX(parentAnchor, keepPixelPosition);
11639  bool successY = setParentAnchorY(parentAnchor, keepPixelPosition);
11640  return successX && successY;
11641 }
11642 
11651 {
11652  // make sure self is not assigned as parent:
11653  if (parentAnchor == this)
11654  {
11655  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
11656  return false;
11657  }
11658  // make sure no recursive parent-child-relationships are created:
11659  QCPItemAnchor *currentParent = parentAnchor;
11660  while (currentParent)
11661  {
11662  if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
11663  {
11664  // is a QCPItemPosition, might have further parent, so keep iterating
11665  if (currentParentPos == this)
11666  {
11667  qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
11668  return false;
11669  }
11670  currentParent = currentParentPos->parentAnchorX();
11671  } else
11672  {
11673  // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
11674  // same, to prevent a position being child of an anchor which itself depends on the position,
11675  // because they're both on the same item:
11676  if (currentParent->mParentItem == mParentItem)
11677  {
11678  qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
11679  return false;
11680  }
11681  break;
11682  }
11683  }
11684 
11685  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
11688 
11689  // save pixel position:
11690  QPointF pixelP;
11691  if (keepPixelPosition)
11692  pixelP = pixelPosition();
11693  // unregister at current parent anchor:
11694  if (mParentAnchorX)
11696  // register at new parent anchor:
11697  if (parentAnchor)
11698  parentAnchor->addChildX(this);
11700  // restore pixel position under new parent:
11701  if (keepPixelPosition)
11702  setPixelPosition(pixelP);
11703  else
11704  setCoords(0, coords().y());
11705  return true;
11706 }
11707 
11716 {
11717  // make sure self is not assigned as parent:
11718  if (parentAnchor == this)
11719  {
11720  qDebug() << Q_FUNC_INFO << "can't set self as parent anchor" << reinterpret_cast<quintptr>(parentAnchor);
11721  return false;
11722  }
11723  // make sure no recursive parent-child-relationships are created:
11724  QCPItemAnchor *currentParent = parentAnchor;
11725  while (currentParent)
11726  {
11727  if (QCPItemPosition *currentParentPos = currentParent->toQCPItemPosition())
11728  {
11729  // is a QCPItemPosition, might have further parent, so keep iterating
11730  if (currentParentPos == this)
11731  {
11732  qDebug() << Q_FUNC_INFO << "can't create recursive parent-child-relationship" << reinterpret_cast<quintptr>(parentAnchor);
11733  return false;
11734  }
11735  currentParent = currentParentPos->parentAnchorY();
11736  } else
11737  {
11738  // is a QCPItemAnchor, can't have further parent. Now make sure the parent items aren't the
11739  // same, to prevent a position being child of an anchor which itself depends on the position,
11740  // because they're both on the same item:
11741  if (currentParent->mParentItem == mParentItem)
11742  {
11743  qDebug() << Q_FUNC_INFO << "can't set parent to be an anchor which itself depends on this position" << reinterpret_cast<quintptr>(parentAnchor);
11744  return false;
11745  }
11746  break;
11747  }
11748  }
11749 
11750  // if previously no parent set and PosType is still ptPlotCoords, set to ptAbsolute:
11753 
11754  // save pixel position:
11755  QPointF pixelP;
11756  if (keepPixelPosition)
11757  pixelP = pixelPosition();
11758  // unregister at current parent anchor:
11759  if (mParentAnchorY)
11761  // register at new parent anchor:
11762  if (parentAnchor)
11763  parentAnchor->addChildY(this);
11765  // restore pixel position under new parent:
11766  if (keepPixelPosition)
11767  setPixelPosition(pixelP);
11768  else
11769  setCoords(coords().x(), 0);
11770  return true;
11771 }
11772 
11791 {
11792  mKey = key;
11793  mValue = value;
11794 }
11795 
11801 void QCPItemPosition::setCoords(const QPointF &pos)
11802 {
11803  setCoords(pos.x(), pos.y());
11804 }
11805 
11813 {
11814  QPointF result;
11815 
11816  // determine X:
11817  switch (mPositionTypeX)
11818  {
11819  case ptAbsolute:
11820  {
11821  result.rx() = mKey;
11822  if (mParentAnchorX)
11823  result.rx() += mParentAnchorX->pixelPosition().x();
11824  break;
11825  }
11826  case ptViewportRatio:
11827  {
11828  result.rx() = mKey*mParentPlot->viewport().width();
11829  if (mParentAnchorX)
11830  result.rx() += mParentAnchorX->pixelPosition().x();
11831  else
11832  result.rx() += mParentPlot->viewport().left();
11833  break;
11834  }
11835  case ptAxisRectRatio:
11836  {
11837  if (mAxisRect)
11838  {
11839  result.rx() = mKey*mAxisRect.data()->width();
11840  if (mParentAnchorX)
11841  result.rx() += mParentAnchorX->pixelPosition().x();
11842  else
11843  result.rx() += mAxisRect.data()->left();
11844  } else
11845  qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
11846  break;
11847  }
11848  case ptPlotCoords:
11849  {
11850  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
11851  result.rx() = mKeyAxis.data()->coordToPixel(mKey);
11852  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
11853  result.rx() = mValueAxis.data()->coordToPixel(mValue);
11854  else
11855  qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
11856  break;
11857  }
11858  }
11859 
11860  // determine Y:
11861  switch (mPositionTypeY)
11862  {
11863  case ptAbsolute:
11864  {
11865  result.ry() = mValue;
11866  if (mParentAnchorY)
11867  result.ry() += mParentAnchorY->pixelPosition().y();
11868  break;
11869  }
11870  case ptViewportRatio:
11871  {
11872  result.ry() = mValue*mParentPlot->viewport().height();
11873  if (mParentAnchorY)
11874  result.ry() += mParentAnchorY->pixelPosition().y();
11875  else
11876  result.ry() += mParentPlot->viewport().top();
11877  break;
11878  }
11879  case ptAxisRectRatio:
11880  {
11881  if (mAxisRect)
11882  {
11883  result.ry() = mValue*mAxisRect.data()->height();
11884  if (mParentAnchorY)
11885  result.ry() += mParentAnchorY->pixelPosition().y();
11886  else
11887  result.ry() += mAxisRect.data()->top();
11888  } else
11889  qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
11890  break;
11891  }
11892  case ptPlotCoords:
11893  {
11894  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
11895  result.ry() = mKeyAxis.data()->coordToPixel(mKey);
11896  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
11897  result.ry() = mValueAxis.data()->coordToPixel(mValue);
11898  else
11899  qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
11900  break;
11901  }
11902  }
11903 
11904  return result;
11905 }
11906 
11913 {
11914  mKeyAxis = keyAxis;
11916 }
11917 
11924 {
11925  mAxisRect = axisRect;
11926 }
11927 
11939 {
11940  double x = pixelPosition.x();
11941  double y = pixelPosition.y();
11942 
11943  switch (mPositionTypeX)
11944  {
11945  case ptAbsolute:
11946  {
11947  if (mParentAnchorX)
11948  x -= mParentAnchorX->pixelPosition().x();
11949  break;
11950  }
11951  case ptViewportRatio:
11952  {
11953  if (mParentAnchorX)
11954  x -= mParentAnchorX->pixelPosition().x();
11955  else
11956  x -= mParentPlot->viewport().left();
11957  x /= (double)mParentPlot->viewport().width();
11958  break;
11959  }
11960  case ptAxisRectRatio:
11961  {
11962  if (mAxisRect)
11963  {
11964  if (mParentAnchorX)
11965  x -= mParentAnchorX->pixelPosition().x();
11966  else
11967  x -= mAxisRect.data()->left();
11968  x /= (double)mAxisRect.data()->width();
11969  } else
11970  qDebug() << Q_FUNC_INFO << "Item position type x is ptAxisRectRatio, but no axis rect was defined";
11971  break;
11972  }
11973  case ptPlotCoords:
11974  {
11975  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Horizontal)
11976  x = mKeyAxis.data()->pixelToCoord(x);
11977  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Horizontal)
11978  y = mValueAxis.data()->pixelToCoord(x);
11979  else
11980  qDebug() << Q_FUNC_INFO << "Item position type x is ptPlotCoords, but no axes were defined";
11981  break;
11982  }
11983  }
11984 
11985  switch (mPositionTypeY)
11986  {
11987  case ptAbsolute:
11988  {
11989  if (mParentAnchorY)
11990  y -= mParentAnchorY->pixelPosition().y();
11991  break;
11992  }
11993  case ptViewportRatio:
11994  {
11995  if (mParentAnchorY)
11996  y -= mParentAnchorY->pixelPosition().y();
11997  else
11998  y -= mParentPlot->viewport().top();
11999  y /= (double)mParentPlot->viewport().height();
12000  break;
12001  }
12002  case ptAxisRectRatio:
12003  {
12004  if (mAxisRect)
12005  {
12006  if (mParentAnchorY)
12007  y -= mParentAnchorY->pixelPosition().y();
12008  else
12009  y -= mAxisRect.data()->top();
12010  y /= (double)mAxisRect.data()->height();
12011  } else
12012  qDebug() << Q_FUNC_INFO << "Item position type y is ptAxisRectRatio, but no axis rect was defined";
12013  break;
12014  }
12015  case ptPlotCoords:
12016  {
12017  if (mKeyAxis && mKeyAxis.data()->orientation() == Qt::Vertical)
12018  x = mKeyAxis.data()->pixelToCoord(y);
12019  else if (mValueAxis && mValueAxis.data()->orientation() == Qt::Vertical)
12020  y = mValueAxis.data()->pixelToCoord(y);
12021  else
12022  qDebug() << Q_FUNC_INFO << "Item position type y is ptPlotCoords, but no axes were defined";
12023  break;
12024  }
12025  }
12026 
12027  setCoords(x, y);
12028 }
12029 
12030 
12034 
12160 /* start of documentation of inline functions */
12161 
12177 /* end of documentation of inline functions */
12178 /* start documentation of pure virtual functions */
12179 
12190 /* end documentation of pure virtual functions */
12191 /* start documentation of signals */
12192 
12198 /* end documentation of signals */
12199 
12204  QCPLayerable(parentPlot),
12205  mClipToAxisRect(false),
12206  mSelectable(true),
12207  mSelected(false)
12208 {
12209  parentPlot->registerItem(this);
12210 
12211  QList<QCPAxisRect*> rects = parentPlot->axisRects();
12212  if (rects.size() > 0)
12213  {
12214  setClipToAxisRect(true);
12215  setClipAxisRect(rects.first());
12216  }
12217 }
12218 
12220 {
12221  // don't delete mPositions because every position is also an anchor and thus in mAnchors
12222  qDeleteAll(mAnchors);
12223 }
12224 
12225 /* can't make this a header inline function, because QPointer breaks with forward declared types, see QTBUG-29588 */
12227 {
12228  return mClipAxisRect.data();
12229 }
12230 
12238 {
12239  mClipToAxisRect = clip;
12240  if (mClipToAxisRect)
12242 }
12243 
12251 {
12252  mClipAxisRect = rect;
12253  if (mClipToAxisRect)
12255 }
12256 
12267 {
12268  if (mSelectable != selectable)
12269  {
12272  }
12273 }
12274 
12290 {
12291  if (mSelected != selected)
12292  {
12293  mSelected = selected;
12295  }
12296 }
12297 
12308 QCPItemPosition *QCPAbstractItem::position(const QString &name) const
12309 {
12310  for (int i=0; i<mPositions.size(); ++i)
12311  {
12312  if (mPositions.at(i)->name() == name)
12313  return mPositions.at(i);
12314  }
12315  qDebug() << Q_FUNC_INFO << "position with name not found:" << name;
12316  return 0;
12317 }
12318 
12329 QCPItemAnchor *QCPAbstractItem::anchor(const QString &name) const
12330 {
12331  for (int i=0; i<mAnchors.size(); ++i)
12332  {
12333  if (mAnchors.at(i)->name() == name)
12334  return mAnchors.at(i);
12335  }
12336  qDebug() << Q_FUNC_INFO << "anchor with name not found:" << name;
12337  return 0;
12338 }
12339 
12348 bool QCPAbstractItem::hasAnchor(const QString &name) const
12349 {
12350  for (int i=0; i<mAnchors.size(); ++i)
12351  {
12352  if (mAnchors.at(i)->name() == name)
12353  return true;
12354  }
12355  return false;
12356 }
12357 
12368 {
12370  return mClipAxisRect.data()->rect();
12371  else
12372  return mParentPlot->viewport();
12373 }
12374 
12389 {
12391 }
12392 
12406 double QCPAbstractItem::rectDistance(const QRectF &rect, const QPointF &pos, bool filledRect) const
12407 {
12408  double result = -1;
12409 
12410  // distance to border:
12411  QList<QLineF> lines;
12412  lines << QLineF(rect.topLeft(), rect.topRight()) << QLineF(rect.bottomLeft(), rect.bottomRight())
12413  << QLineF(rect.topLeft(), rect.bottomLeft()) << QLineF(rect.topRight(), rect.bottomRight());
12414  double minDistSqr = std::numeric_limits<double>::max();
12415  for (int i=0; i<lines.size(); ++i)
12416  {
12417  double distSqr = QCPVector2D(pos).distanceSquaredToLine(lines.at(i).p1(), lines.at(i).p2());
12418  if (distSqr < minDistSqr)
12419  minDistSqr = distSqr;
12420  }
12421  result = qSqrt(minDistSqr);
12422 
12423  // filled rect, allow click inside to count as hit:
12424  if (filledRect && result > mParentPlot->selectionTolerance()*0.99)
12425  {
12426  if (rect.contains(pos))
12427  result = mParentPlot->selectionTolerance()*0.99;
12428  }
12429  return result;
12430 }
12431 
12442 QPointF QCPAbstractItem::anchorPixelPosition(int anchorId) const
12443 {
12444  qDebug() << Q_FUNC_INFO << "called on item which shouldn't have any anchors (this method not reimplemented). anchorId" << anchorId;
12445  return QPointF();
12446 }
12447 
12463 {
12464  if (hasAnchor(name))
12465  qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
12466  QCPItemPosition *newPosition = new QCPItemPosition(mParentPlot, this, name);
12467  mPositions.append(newPosition);
12468  mAnchors.append(newPosition); // every position is also an anchor
12469  newPosition->setAxes(mParentPlot->xAxis, mParentPlot->yAxis);
12470  newPosition->setType(QCPItemPosition::ptPlotCoords);
12471  if (mParentPlot->axisRect())
12472  newPosition->setAxisRect(mParentPlot->axisRect());
12473  newPosition->setCoords(0, 0);
12474  return newPosition;
12475 }
12476 
12496 QCPItemAnchor *QCPAbstractItem::createAnchor(const QString &name, int anchorId)
12497 {
12498  if (hasAnchor(name))
12499  qDebug() << Q_FUNC_INFO << "anchor/position with name exists already:" << name;
12500  QCPItemAnchor *newAnchor = new QCPItemAnchor(mParentPlot, this, name, anchorId);
12501  mAnchors.append(newAnchor);
12502  return newAnchor;
12503 }
12504 
12505 /* inherits documentation from base class */
12506 void QCPAbstractItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
12507 {
12508  Q_UNUSED(event)
12509  Q_UNUSED(details)
12510  if (mSelectable)
12511  {
12512  bool selBefore = mSelected;
12513  setSelected(additive ? !mSelected : true);
12514  if (selectionStateChanged)
12515  *selectionStateChanged = mSelected != selBefore;
12516  }
12517 }
12518 
12519 /* inherits documentation from base class */
12520 void QCPAbstractItem::deselectEvent(bool *selectionStateChanged)
12521 {
12522  if (mSelectable)
12523  {
12524  bool selBefore = mSelected;
12525  setSelected(false);
12526  if (selectionStateChanged)
12527  *selectionStateChanged = mSelected != selBefore;
12528  }
12529 }
12530 
12531 /* inherits documentation from base class */
12533 {
12534  return QCP::iSelectItems;
12535 }
12536 /* end of 'src/item.cpp' */
12537 
12538 
12539 /* including file 'src/core.cpp', size 125037 */
12540 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
12541 
12545 
12555 /* start of documentation of inline functions */
12556 
12571 /* end of documentation of inline functions */
12572 /* start of documentation of signals */
12573 
12744 /* end of documentation of signals */
12745 /* start of documentation of public members */
12746 
12843 /* end of documentation of public members */
12844 
12848 QCustomPlot::QCustomPlot(QWidget *parent) :
12849  QWidget(parent),
12850  xAxis(0),
12851  yAxis(0),
12852  xAxis2(0),
12853  yAxis2(0),
12854  legend(0),
12855  mBufferDevicePixelRatio(1.0), // will be adapted to primary screen below
12856  mPlotLayout(0),
12857  mAutoAddPlottableToLegend(true),
12858  mAntialiasedElements(QCP::aeNone),
12859  mNotAntialiasedElements(QCP::aeNone),
12860  mInteractions(0),
12861  mSelectionTolerance(8),
12862  mNoAntialiasingOnDrag(false),
12863  mBackgroundBrush(Qt::white, Qt::SolidPattern),
12864  mBackgroundScaled(true),
12865  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
12866  mCurrentLayer(0),
12867  mPlottingHints(QCP::phCacheLabels|QCP::phImmediateRefresh),
12868  mMultiSelectModifier(Qt::ControlModifier),
12869  mSelectionRectMode(QCP::srmNone),
12870  mSelectionRect(0),
12871  mOpenGl(false),
12872  mMouseHasMoved(false),
12873  mMouseEventLayerable(0),
12874  mMouseSignalLayerable(0),
12875  mReplotting(false),
12876  mReplotQueued(false),
12877  mOpenGlMultisamples(16),
12878  mOpenGlAntialiasedElementsBackup(QCP::aeNone),
12879  mOpenGlCacheLabelsBackup(true)
12880 {
12881  setAttribute(Qt::WA_NoMousePropagation);
12882  setAttribute(Qt::WA_OpaquePaintEvent);
12883  setFocusPolicy(Qt::ClickFocus);
12884  setMouseTracking(true);
12885  QLocale currentLocale = locale();
12886  currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
12887  setLocale(currentLocale);
12888 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
12889 # ifdef QCP_DEVICEPIXELRATIO_FLOAT
12890  setBufferDevicePixelRatio(QWidget::devicePixelRatioF());
12891 # else
12892  setBufferDevicePixelRatio(QWidget::devicePixelRatio());
12893 # endif
12894 #endif
12895 
12898  // create initial layers:
12899  mLayers.append(new QCPLayer(this, QLatin1String("background")));
12900  mLayers.append(new QCPLayer(this, QLatin1String("grid")));
12901  mLayers.append(new QCPLayer(this, QLatin1String("main")));
12902  mLayers.append(new QCPLayer(this, QLatin1String("axes")));
12903  mLayers.append(new QCPLayer(this, QLatin1String("legend")));
12904  mLayers.append(new QCPLayer(this, QLatin1String("overlay")));
12906  setCurrentLayer(QLatin1String("main"));
12907  layer(QLatin1String("overlay"))->setMode(QCPLayer::lmBuffered);
12908 
12909  // create initial layout, axis rect and legend:
12910  mPlotLayout = new QCPLayoutGrid;
12912  mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
12913  mPlotLayout->setLayer(QLatin1String("main"));
12914  QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
12915  mPlotLayout->addElement(0, 0, defaultAxisRect);
12916  xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
12917  yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
12918  xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
12919  yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
12920  legend = new QCPLegend;
12921  legend->setVisible(false);
12922  defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
12923  defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
12924 
12925  defaultAxisRect->setLayer(QLatin1String("background"));
12926  xAxis->setLayer(QLatin1String("axes"));
12927  yAxis->setLayer(QLatin1String("axes"));
12928  xAxis2->setLayer(QLatin1String("axes"));
12929  yAxis2->setLayer(QLatin1String("axes"));
12930  xAxis->grid()->setLayer(QLatin1String("grid"));
12931  yAxis->grid()->setLayer(QLatin1String("grid"));
12932  xAxis2->grid()->setLayer(QLatin1String("grid"));
12933  yAxis2->grid()->setLayer(QLatin1String("grid"));
12934  legend->setLayer(QLatin1String("legend"));
12935 
12936  // create selection rect instance:
12937  mSelectionRect = new QCPSelectionRect(this);
12938  mSelectionRect->setLayer(QLatin1String("overlay"));
12939 
12940  setViewport(rect()); // needs to be called after mPlotLayout has been created
12941 
12943 }
12944 
12946 {
12947  clearPlottables();
12948  clearItems();
12949 
12950  if (mPlotLayout)
12951  {
12952  delete mPlotLayout;
12953  mPlotLayout = 0;
12954  }
12955 
12956  mCurrentLayer = 0;
12957  qDeleteAll(mLayers); // don't use removeLayer, because it would prevent the last layer to be removed
12958  mLayers.clear();
12959 }
12960 
12978 void QCustomPlot::setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
12979 {
12981 
12982  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
12984  mNotAntialiasedElements |= ~mAntialiasedElements;
12985 }
12986 
12994 void QCustomPlot::setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled)
12995 {
12996  if (!enabled && mAntialiasedElements.testFlag(antialiasedElement))
12997  mAntialiasedElements &= ~antialiasedElement;
12998  else if (enabled && !mAntialiasedElements.testFlag(antialiasedElement))
12999  mAntialiasedElements |= antialiasedElement;
13000 
13001  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
13003  mNotAntialiasedElements |= ~mAntialiasedElements;
13004 }
13005 
13025 {
13027 
13028  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
13030  mAntialiasedElements |= ~mNotAntialiasedElements;
13031 }
13032 
13040 void QCustomPlot::setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled)
13041 {
13042  if (!enabled && mNotAntialiasedElements.testFlag(notAntialiasedElement))
13043  mNotAntialiasedElements &= ~notAntialiasedElement;
13044  else if (enabled && !mNotAntialiasedElements.testFlag(notAntialiasedElement))
13045  mNotAntialiasedElements |= notAntialiasedElement;
13046 
13047  // make sure elements aren't in mNotAntialiasedElements and mAntialiasedElements simultaneously:
13049  mAntialiasedElements |= ~mNotAntialiasedElements;
13050 }
13051 
13059 {
13061 }
13062 
13117 void QCustomPlot::setInteractions(const QCP::Interactions &interactions)
13118 {
13120 }
13121 
13129 void QCustomPlot::setInteraction(const QCP::Interaction &interaction, bool enabled)
13130 {
13131  if (!enabled && mInteractions.testFlag(interaction))
13132  mInteractions &= ~interaction;
13133  else if (enabled && !mInteractions.testFlag(interaction))
13134  mInteractions |= interaction;
13135 }
13136 
13151 {
13152  mSelectionTolerance = pixels;
13153 }
13154 
13165 {
13166  mNoAntialiasingOnDrag = enabled;
13167 }
13168 
13174 void QCustomPlot::setPlottingHints(const QCP::PlottingHints &hints)
13175 {
13176  mPlottingHints = hints;
13177 }
13178 
13185 {
13186  QCP::PlottingHints newHints = mPlottingHints;
13187  if (!enabled)
13188  newHints &= ~hint;
13189  else
13190  newHints |= hint;
13191 
13192  if (newHints != mPlottingHints)
13193  setPlottingHints(newHints);
13194 }
13195 
13206 void QCustomPlot::setMultiSelectModifier(Qt::KeyboardModifier modifier)
13207 {
13208  mMultiSelectModifier = modifier;
13209 }
13210 
13232 {
13233  if (mSelectionRect)
13234  {
13235  if (mode == QCP::srmNone)
13236  mSelectionRect->cancel(); // when switching to none, we immediately want to abort a potentially active selection rect
13237 
13238  // disconnect old connections:
13240  disconnect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));
13241  else if (mSelectionRectMode == QCP::srmZoom)
13242  disconnect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));
13243 
13244  // establish new ones:
13245  if (mode == QCP::srmSelect)
13246  connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));
13247  else if (mode == QCP::srmZoom)
13248  connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));
13249  }
13250 
13251  mSelectionRectMode = mode;
13252 }
13253 
13265 {
13266  if (mSelectionRect)
13267  delete mSelectionRect;
13268 
13270 
13271  if (mSelectionRect)
13272  {
13273  // establish connections with new selection rect:
13275  connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectSelection(QRect,QMouseEvent*)));
13276  else if (mSelectionRectMode == QCP::srmZoom)
13277  connect(mSelectionRect, SIGNAL(accepted(QRect,QMouseEvent*)), this, SLOT(processRectZoom(QRect,QMouseEvent*)));
13278  }
13279 }
13280 
13317 void QCustomPlot::setOpenGl(bool enabled, int multisampling)
13318 {
13319  mOpenGlMultisamples = qMax(0, multisampling);
13320 #ifdef QCUSTOMPLOT_USE_OPENGL
13321  mOpenGl = enabled;
13322  if (mOpenGl)
13323  {
13324  if (setupOpenGl())
13325  {
13326  // backup antialiasing override and labelcaching setting so we can restore upon disabling OpenGL
13329  // set antialiasing override to antialias all (aligns gl pixel grid properly), and disable label caching (would use software rasterizer for pixmap caches):
13332  } else
13333  {
13334  qDebug() << Q_FUNC_INFO << "Failed to enable OpenGL, continuing plotting without hardware acceleration.";
13335  mOpenGl = false;
13336  }
13337  } else
13338  {
13339  // restore antialiasing override and labelcaching to what it was before enabling OpenGL, if nobody changed it in the meantime:
13342  if (!mPlottingHints.testFlag(QCP::phCacheLabels))
13344  freeOpenGl();
13345  }
13346  // recreate all paint buffers:
13347  mPaintBuffers.clear();
13349 #else
13350  Q_UNUSED(enabled)
13351  qDebug() << Q_FUNC_INFO << "QCustomPlot can't use OpenGL because QCUSTOMPLOT_USE_OPENGL was not defined during compilation (add 'DEFINES += QCUSTOMPLOT_USE_OPENGL' to your qmake .pro file)";
13352 #endif
13353 }
13354 
13371 void QCustomPlot::setViewport(const QRect &rect)
13372 {
13373  mViewport = rect;
13374  if (mPlotLayout)
13376 }
13377 
13390 {
13391  if (!qFuzzyCompare(ratio, mBufferDevicePixelRatio))
13392  {
13393 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
13394  mBufferDevicePixelRatio = ratio;
13395  for (int i=0; i<mPaintBuffers.size(); ++i)
13396  mPaintBuffers.at(i)->setDevicePixelRatio(mBufferDevicePixelRatio);
13397  // Note: axis label cache has devicePixelRatio as part of cache hash, so no need to manually clear cache here
13398 #else
13399  qDebug() << Q_FUNC_INFO << "Device pixel ratios not supported for Qt versions before 5.4";
13401 #endif
13402  }
13403 }
13404 
13420 void QCustomPlot::setBackground(const QPixmap &pm)
13421 {
13422  mBackgroundPixmap = pm;
13423  mScaledBackgroundPixmap = QPixmap();
13424 }
13425 
13439 void QCustomPlot::setBackground(const QBrush &brush)
13440 {
13441  mBackgroundBrush = brush;
13442 }
13443 
13451 void QCustomPlot::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
13452 {
13453  mBackgroundPixmap = pm;
13454  mScaledBackgroundPixmap = QPixmap();
13455  mBackgroundScaled = scaled;
13456  mBackgroundScaledMode = mode;
13457 }
13458 
13470 {
13471  mBackgroundScaled = scaled;
13472 }
13473 
13480 void QCustomPlot::setBackgroundScaledMode(Qt::AspectRatioMode mode)
13481 {
13482  mBackgroundScaledMode = mode;
13483 }
13484 
13494 {
13495  if (index >= 0 && index < mPlottables.size())
13496  {
13497  return mPlottables.at(index);
13498  } else
13499  {
13500  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13501  return 0;
13502  }
13503 }
13504 
13513 {
13514  if (!mPlottables.isEmpty())
13515  {
13516  return mPlottables.last();
13517  } else
13518  return 0;
13519 }
13520 
13530 {
13531  if (!mPlottables.contains(plottable))
13532  {
13533  qDebug() << Q_FUNC_INFO << "plottable not in list:" << reinterpret_cast<quintptr>(plottable);
13534  return false;
13535  }
13536 
13537  // remove plottable from legend:
13538  plottable->removeFromLegend();
13539  // special handling for QCPGraphs to maintain the simple graph interface:
13540  if (QCPGraph *graph = qobject_cast<QCPGraph*>(plottable))
13541  mGraphs.removeOne(graph);
13542  // remove plottable:
13543  delete plottable;
13544  mPlottables.removeOne(plottable);
13545  return true;
13546 }
13547 
13553 {
13554  if (index >= 0 && index < mPlottables.size())
13555  return removePlottable(mPlottables[index]);
13556  else
13557  {
13558  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13559  return false;
13560  }
13561 }
13562 
13572 {
13573  int c = mPlottables.size();
13574  for (int i=c-1; i >= 0; --i)
13576  return c;
13577 }
13578 
13585 {
13586  return mPlottables.size();
13587 }
13588 
13596 QList<QCPAbstractPlottable*> QCustomPlot::selectedPlottables() const
13597 {
13598  QList<QCPAbstractPlottable*> result;
13600  {
13601  if (plottable->selected())
13602  result.append(plottable);
13603  }
13604  return result;
13605 }
13606 
13619 QCPAbstractPlottable *QCustomPlot::plottableAt(const QPointF &pos, bool onlySelectable) const
13620 {
13621  QCPAbstractPlottable *resultPlottable = 0;
13622  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
13623 
13625  {
13626  if (onlySelectable && !plottable->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPabstractPlottable::selectable
13627  continue;
13628  if ((plottable->keyAxis()->axisRect()->rect() & plottable->valueAxis()->axisRect()->rect()).contains(pos.toPoint())) // only consider clicks inside the rect that is spanned by the plottable's key/value axes
13629  {
13630  double currentDistance = plottable->selectTest(pos, false);
13631  if (currentDistance >= 0 && currentDistance < resultDistance)
13632  {
13633  resultPlottable = plottable;
13634  resultDistance = currentDistance;
13635  }
13636  }
13637  }
13638 
13639  return resultPlottable;
13640 }
13641 
13646 {
13647  return mPlottables.contains(plottable);
13648 }
13649 
13658 QCPGraph *QCustomPlot::graph(int index) const
13659 {
13660  if (index >= 0 && index < mGraphs.size())
13661  {
13662  return mGraphs.at(index);
13663  } else
13664  {
13665  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13666  return 0;
13667  }
13668 }
13669 
13678 {
13679  if (!mGraphs.isEmpty())
13680  {
13681  return mGraphs.last();
13682  } else
13683  return 0;
13684 }
13685 
13699 {
13700  if (!keyAxis) keyAxis = xAxis;
13701  if (!valueAxis) valueAxis = yAxis;
13702  if (!keyAxis || !valueAxis)
13703  {
13704  qDebug() << Q_FUNC_INFO << "can't use default QCustomPlot xAxis or yAxis, because at least one is invalid (has been deleted)";
13705  return 0;
13706  }
13707  if (keyAxis->parentPlot() != this || valueAxis->parentPlot() != this)
13708  {
13709  qDebug() << Q_FUNC_INFO << "passed keyAxis or valueAxis doesn't have this QCustomPlot as parent";
13710  return 0;
13711  }
13712 
13713  QCPGraph *newGraph = new QCPGraph(keyAxis, valueAxis);
13714  newGraph->setName(QLatin1String("Graph ")+QString::number(mGraphs.size()));
13715  return newGraph;
13716 }
13717 
13729 {
13730  return removePlottable(graph);
13731 }
13732 
13738 {
13739  if (index >= 0 && index < mGraphs.size())
13740  return removeGraph(mGraphs[index]);
13741  else
13742  return false;
13743 }
13744 
13754 {
13755  int c = mGraphs.size();
13756  for (int i=c-1; i >= 0; --i)
13757  removeGraph(mGraphs[i]);
13758  return c;
13759 }
13760 
13767 {
13768  return mGraphs.size();
13769 }
13770 
13779 QList<QCPGraph*> QCustomPlot::selectedGraphs() const
13780 {
13781  QList<QCPGraph*> result;
13782  foreach (QCPGraph *graph, mGraphs)
13783  {
13784  if (graph->selected())
13785  result.append(graph);
13786  }
13787  return result;
13788 }
13789 
13799 {
13800  if (index >= 0 && index < mItems.size())
13801  {
13802  return mItems.at(index);
13803  } else
13804  {
13805  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13806  return 0;
13807  }
13808 }
13809 
13818 {
13819  if (!mItems.isEmpty())
13820  {
13821  return mItems.last();
13822  } else
13823  return 0;
13824 }
13825 
13834 {
13835  if (mItems.contains(item))
13836  {
13837  delete item;
13838  mItems.removeOne(item);
13839  return true;
13840  } else
13841  {
13842  qDebug() << Q_FUNC_INFO << "item not in list:" << reinterpret_cast<quintptr>(item);
13843  return false;
13844  }
13845 }
13846 
13852 {
13853  if (index >= 0 && index < mItems.size())
13854  return removeItem(mItems[index]);
13855  else
13856  {
13857  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13858  return false;
13859  }
13860 }
13861 
13870 {
13871  int c = mItems.size();
13872  for (int i=c-1; i >= 0; --i)
13873  removeItem(mItems[i]);
13874  return c;
13875 }
13876 
13883 {
13884  return mItems.size();
13885 }
13886 
13892 QList<QCPAbstractItem*> QCustomPlot::selectedItems() const
13893 {
13894  QList<QCPAbstractItem*> result;
13895  foreach (QCPAbstractItem *item, mItems)
13896  {
13897  if (item->selected())
13898  result.append(item);
13899  }
13900  return result;
13901 }
13902 
13916 QCPAbstractItem *QCustomPlot::itemAt(const QPointF &pos, bool onlySelectable) const
13917 {
13918  QCPAbstractItem *resultItem = 0;
13919  double resultDistance = mSelectionTolerance; // only regard clicks with distances smaller than mSelectionTolerance as selections, so initialize with that value
13920 
13921  foreach (QCPAbstractItem *item, mItems)
13922  {
13923  if (onlySelectable && !item->selectable()) // we could have also passed onlySelectable to the selectTest function, but checking here is faster, because we have access to QCPAbstractItem::selectable
13924  continue;
13925  if (!item->clipToAxisRect() || item->clipRect().contains(pos.toPoint())) // only consider clicks inside axis cliprect of the item if actually clipped to it
13926  {
13927  double currentDistance = item->selectTest(pos, false);
13928  if (currentDistance >= 0 && currentDistance < resultDistance)
13929  {
13930  resultItem = item;
13931  resultDistance = currentDistance;
13932  }
13933  }
13934  }
13935 
13936  return resultItem;
13937 }
13938 
13945 {
13946  return mItems.contains(item);
13947 }
13948 
13957 QCPLayer *QCustomPlot::layer(const QString &name) const
13958 {
13959  foreach (QCPLayer *layer, mLayers)
13960  {
13961  if (layer->name() == name)
13962  return layer;
13963  }
13964  return 0;
13965 }
13966 
13973 QCPLayer *QCustomPlot::layer(int index) const
13974 {
13975  if (index >= 0 && index < mLayers.size())
13976  {
13977  return mLayers.at(index);
13978  } else
13979  {
13980  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
13981  return 0;
13982  }
13983 }
13984 
13989 {
13990  return mCurrentLayer;
13991 }
13992 
14003 bool QCustomPlot::setCurrentLayer(const QString &name)
14004 {
14005  if (QCPLayer *newCurrentLayer = layer(name))
14006  {
14007  return setCurrentLayer(newCurrentLayer);
14008  } else
14009  {
14010  qDebug() << Q_FUNC_INFO << "layer with name doesn't exist:" << name;
14011  return false;
14012  }
14013 }
14014 
14024 {
14025  if (!mLayers.contains(layer))
14026  {
14027  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
14028  return false;
14029  }
14030 
14031  mCurrentLayer = layer;
14032  return true;
14033 }
14034 
14041 {
14042  return mLayers.size();
14043 }
14044 
14058 bool QCustomPlot::addLayer(const QString &name, QCPLayer *otherLayer, QCustomPlot::LayerInsertMode insertMode)
14059 {
14060  if (!otherLayer)
14061  otherLayer = mLayers.last();
14062  if (!mLayers.contains(otherLayer))
14063  {
14064  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
14065  return false;
14066  }
14067  if (layer(name))
14068  {
14069  qDebug() << Q_FUNC_INFO << "A layer exists already with the name" << name;
14070  return false;
14071  }
14072 
14073  QCPLayer *newLayer = new QCPLayer(this, name);
14074  mLayers.insert(otherLayer->index() + (insertMode==limAbove ? 1:0), newLayer);
14076  setupPaintBuffers(); // associates new layer with the appropriate paint buffer
14077  return true;
14078 }
14079 
14095 {
14096  if (!mLayers.contains(layer))
14097  {
14098  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
14099  return false;
14100  }
14101  if (mLayers.size() < 2)
14102  {
14103  qDebug() << Q_FUNC_INFO << "can't remove last layer";
14104  return false;
14105  }
14106 
14107  // append all children of this layer to layer below (if this is lowest layer, prepend to layer above)
14108  int removedIndex = layer->index();
14109  bool isFirstLayer = removedIndex==0;
14110  QCPLayer *targetLayer = isFirstLayer ? mLayers.at(removedIndex+1) : mLayers.at(removedIndex-1);
14111  QList<QCPLayerable*> children = layer->children();
14112  if (isFirstLayer) // prepend in reverse order (so order relative to each other stays the same)
14113  {
14114  for (int i=children.size()-1; i>=0; --i)
14115  children.at(i)->moveToLayer(targetLayer, true);
14116  } else // append normally
14117  {
14118  for (int i=0; i<children.size(); ++i)
14119  children.at(i)->moveToLayer(targetLayer, false);
14120  }
14121  // if removed layer is current layer, change current layer to layer below/above:
14122  if (layer == mCurrentLayer)
14123  setCurrentLayer(targetLayer);
14124  // invalidate the paint buffer that was responsible for this layer:
14125  if (!layer->mPaintBuffer.isNull())
14126  layer->mPaintBuffer.data()->setInvalidated();
14127  // remove layer:
14128  delete layer;
14129  mLayers.removeOne(layer);
14131  return true;
14132 }
14133 
14144 {
14145  if (!mLayers.contains(layer))
14146  {
14147  qDebug() << Q_FUNC_INFO << "layer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(layer);
14148  return false;
14149  }
14150  if (!mLayers.contains(otherLayer))
14151  {
14152  qDebug() << Q_FUNC_INFO << "otherLayer not a layer of this QCustomPlot:" << reinterpret_cast<quintptr>(otherLayer);
14153  return false;
14154  }
14155 
14156  if (layer->index() > otherLayer->index())
14157  mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 1:0));
14158  else if (layer->index() < otherLayer->index())
14159  mLayers.move(layer->index(), otherLayer->index() + (insertMode==limAbove ? 0:-1));
14160 
14161  // invalidate the paint buffers that are responsible for the layers:
14162  if (!layer->mPaintBuffer.isNull())
14163  layer->mPaintBuffer.data()->setInvalidated();
14164  if (!otherLayer->mPaintBuffer.isNull())
14165  otherLayer->mPaintBuffer.data()->setInvalidated();
14166 
14168  return true;
14169 }
14170 
14181 {
14182  return axisRects().size();
14183 }
14184 
14195 {
14196  const QList<QCPAxisRect*> rectList = axisRects();
14197  if (index >= 0 && index < rectList.size())
14198  {
14199  return rectList.at(index);
14200  } else
14201  {
14202  qDebug() << Q_FUNC_INFO << "invalid axis rect index" << index;
14203  return 0;
14204  }
14205 }
14206 
14212 QList<QCPAxisRect*> QCustomPlot::axisRects() const
14213 {
14214  QList<QCPAxisRect*> result;
14215  QStack<QCPLayoutElement*> elementStack;
14216  if (mPlotLayout)
14217  elementStack.push(mPlotLayout);
14218 
14219  while (!elementStack.isEmpty())
14220  {
14221  foreach (QCPLayoutElement *element, elementStack.pop()->elements(false))
14222  {
14223  if (element)
14224  {
14225  elementStack.push(element);
14226  if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(element))
14227  result.append(ar);
14228  }
14229  }
14230  }
14231 
14232  return result;
14233 }
14234 
14245 {
14246  QCPLayoutElement *currentElement = mPlotLayout;
14247  bool searchSubElements = true;
14248  while (searchSubElements && currentElement)
14249  {
14250  searchSubElements = false;
14251  foreach (QCPLayoutElement *subElement, currentElement->elements(false))
14252  {
14253  if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
14254  {
14255  currentElement = subElement;
14256  searchSubElements = true;
14257  break;
14258  }
14259  }
14260  }
14261  return currentElement;
14262 }
14263 
14274 QCPAxisRect *QCustomPlot::axisRectAt(const QPointF &pos) const
14275 {
14276  QCPAxisRect *result = 0;
14277  QCPLayoutElement *currentElement = mPlotLayout;
14278  bool searchSubElements = true;
14279  while (searchSubElements && currentElement)
14280  {
14281  searchSubElements = false;
14282  foreach (QCPLayoutElement *subElement, currentElement->elements(false))
14283  {
14284  if (subElement && subElement->realVisibility() && subElement->selectTest(pos, false) >= 0)
14285  {
14286  currentElement = subElement;
14287  searchSubElements = true;
14288  if (QCPAxisRect *ar = qobject_cast<QCPAxisRect*>(currentElement))
14289  result = ar;
14290  break;
14291  }
14292  }
14293  }
14294  return result;
14295 }
14296 
14304 QList<QCPAxis*> QCustomPlot::selectedAxes() const
14305 {
14306  QList<QCPAxis*> result, allAxes;
14307  foreach (QCPAxisRect *rect, axisRects())
14308  allAxes << rect->axes();
14309 
14310  foreach (QCPAxis *axis, allAxes)
14311  {
14312  if (axis->selectedParts() != QCPAxis::spNone)
14313  result.append(axis);
14314  }
14315 
14316  return result;
14317 }
14318 
14326 QList<QCPLegend*> QCustomPlot::selectedLegends() const
14327 {
14328  QList<QCPLegend*> result;
14329 
14330  QStack<QCPLayoutElement*> elementStack;
14331  if (mPlotLayout)
14332  elementStack.push(mPlotLayout);
14333 
14334  while (!elementStack.isEmpty())
14335  {
14336  foreach (QCPLayoutElement *subElement, elementStack.pop()->elements(false))
14337  {
14338  if (subElement)
14339  {
14340  elementStack.push(subElement);
14341  if (QCPLegend *leg = qobject_cast<QCPLegend*>(subElement))
14342  {
14343  if (leg->selectedParts() != QCPLegend::spNone)
14344  result.append(leg);
14345  }
14346  }
14347  }
14348  }
14349 
14350  return result;
14351 }
14352 
14363 {
14364  foreach (QCPLayer *layer, mLayers)
14365  {
14366  foreach (QCPLayerable *layerable, layer->children())
14367  layerable->deselectEvent(0);
14368  }
14369 }
14370 
14397 {
14398  if (refreshPriority == QCustomPlot::rpQueuedReplot)
14399  {
14400  if (!mReplotQueued)
14401  {
14402  mReplotQueued = true;
14403  QTimer::singleShot(0, this, SLOT(replot()));
14404  }
14405  return;
14406  }
14407 
14408  if (mReplotting) // incase signals loop back to replot slot
14409  return;
14410  mReplotting = true;
14411  mReplotQueued = false;
14412  emit beforeReplot();
14413 
14414  updateLayout();
14415  // draw all layered objects (grid, axes, plottables, items, legend,...) into their buffers:
14417  foreach (QCPLayer *layer, mLayers)
14418  layer->drawToPaintBuffer();
14419  for (int i=0; i<mPaintBuffers.size(); ++i)
14420  mPaintBuffers.at(i)->setInvalidated(false);
14421 
14422  if ((refreshPriority == rpRefreshHint && mPlottingHints.testFlag(QCP::phImmediateRefresh)) || refreshPriority==rpImmediateRefresh)
14423  repaint();
14424  else
14425  update();
14426 
14427  emit afterReplot();
14428  mReplotting = false;
14429 }
14430 
14439 void QCustomPlot::rescaleAxes(bool onlyVisiblePlottables)
14440 {
14441  QList<QCPAxis*> allAxes;
14442  foreach (QCPAxisRect *rect, axisRects())
14443  allAxes << rect->axes();
14444 
14445  foreach (QCPAxis *axis, allAxes)
14446  axis->rescale(onlyVisiblePlottables);
14447 }
14448 
14486 bool QCustomPlot::savePdf(const QString &fileName, int width, int height, QCP::ExportPen exportPen, const QString &pdfCreator, const QString &pdfTitle)
14487 {
14488  bool success = false;
14489 #ifdef QT_NO_PRINTER
14490  Q_UNUSED(fileName)
14491  Q_UNUSED(exportPen)
14492  Q_UNUSED(width)
14493  Q_UNUSED(height)
14494  Q_UNUSED(pdfCreator)
14495  Q_UNUSED(pdfTitle)
14496  qDebug() << Q_FUNC_INFO << "Qt was built without printer support (QT_NO_PRINTER). PDF not created.";
14497 #else
14498  int newWidth, newHeight;
14499  if (width == 0 || height == 0)
14500  {
14501  newWidth = this->width();
14502  newHeight = this->height();
14503  } else
14504  {
14505  newWidth = width;
14506  newHeight = height;
14507  }
14508 
14509  QPrinter printer(QPrinter::ScreenResolution);
14510  printer.setOutputFileName(fileName);
14511  printer.setOutputFormat(QPrinter::PdfFormat);
14512  printer.setColorMode(QPrinter::Color);
14513  printer.printEngine()->setProperty(QPrintEngine::PPK_Creator, pdfCreator);
14514  printer.printEngine()->setProperty(QPrintEngine::PPK_DocumentName, pdfTitle);
14515  QRect oldViewport = viewport();
14516  setViewport(QRect(0, 0, newWidth, newHeight));
14517 #if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
14518  printer.setFullPage(true);
14519  printer.setPaperSize(viewport().size(), QPrinter::DevicePixel);
14520 #else
14521  QPageLayout pageLayout;
14522  pageLayout.setMode(QPageLayout::FullPageMode);
14523  pageLayout.setOrientation(QPageLayout::Portrait);
14524  pageLayout.setMargins(QMarginsF(0, 0, 0, 0));
14525  pageLayout.setPageSize(QPageSize(viewport().size(), QPageSize::Point, QString(), QPageSize::ExactMatch));
14526  printer.setPageLayout(pageLayout);
14527 #endif
14528  QCPPainter printpainter;
14529  if (printpainter.begin(&printer))
14530  {
14531  printpainter.setMode(QCPPainter::pmVectorized);
14532  printpainter.setMode(QCPPainter::pmNoCaching);
14533  printpainter.setMode(QCPPainter::pmNonCosmetic, exportPen==QCP::epNoCosmetic);
14534  printpainter.setWindow(mViewport);
14535  if (mBackgroundBrush.style() != Qt::NoBrush &&
14536  mBackgroundBrush.color() != Qt::white &&
14537  mBackgroundBrush.color() != Qt::transparent &&
14538  mBackgroundBrush.color().alpha() > 0) // draw pdf background color if not white/transparent
14539  printpainter.fillRect(viewport(), mBackgroundBrush);
14540  draw(&printpainter);
14541  printpainter.end();
14542  success = true;
14543  }
14544  setViewport(oldViewport);
14545 #endif // QT_NO_PRINTER
14546  return success;
14547 }
14548 
14594 bool QCustomPlot::savePng(const QString &fileName, int width, int height, double scale, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)
14595 {
14596  return saveRastered(fileName, width, height, scale, "PNG", quality, resolution, resolutionUnit);
14597 }
14598 
14641 bool QCustomPlot::saveJpg(const QString &fileName, int width, int height, double scale, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)
14642 {
14643  return saveRastered(fileName, width, height, scale, "JPG", quality, resolution, resolutionUnit);
14644 }
14645 
14685 bool QCustomPlot::saveBmp(const QString &fileName, int width, int height, double scale, int resolution, QCP::ResolutionUnit resolutionUnit)
14686 {
14687  return saveRastered(fileName, width, height, scale, "BMP", -1, resolution, resolutionUnit);
14688 }
14689 
14699 {
14701 }
14702 
14709 {
14711 }
14712 
14718 void QCustomPlot::paintEvent(QPaintEvent *event)
14719 {
14720  Q_UNUSED(event);
14721  QCPPainter painter(this);
14722  if (painter.isActive())
14723  {
14724  painter.setRenderHint(QPainter::HighQualityAntialiasing); // to make Antialiasing look good if using the OpenGL graphicssystem
14725  if (mBackgroundBrush.style() != Qt::NoBrush)
14726  painter.fillRect(mViewport, mBackgroundBrush);
14727  drawBackground(&painter);
14728  for (int bufferIndex = 0; bufferIndex < mPaintBuffers.size(); ++bufferIndex)
14729  mPaintBuffers.at(bufferIndex)->draw(&painter);
14730  }
14731 }
14732 
14738 void QCustomPlot::resizeEvent(QResizeEvent *event)
14739 {
14740  Q_UNUSED(event)
14741  // resize and repaint the buffer:
14742  setViewport(rect());
14743  replot(rpQueuedRefresh); // queued refresh is important here, to prevent painting issues in some contexts (e.g. MDI subwindow)
14744 }
14745 
14755 void QCustomPlot::mouseDoubleClickEvent(QMouseEvent *event)
14756 {
14757  emit mouseDoubleClick(event);
14758  mMouseHasMoved = false;
14759  mMousePressPos = event->pos();
14760 
14761  // determine layerable under the cursor (this event is called instead of the second press event in a double-click):
14762  QList<QVariant> details;
14763  QList<QCPLayerable*> candidates = layerableListAt(mMousePressPos, false, &details);
14764  for (int i=0; i<candidates.size(); ++i)
14765  {
14766  event->accept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list
14767  candidates.at(i)->mouseDoubleClickEvent(event, details.at(i));
14768  if (event->isAccepted())
14769  {
14770  mMouseEventLayerable = candidates.at(i);
14771  mMouseEventLayerableDetails = details.at(i);
14772  break;
14773  }
14774  }
14775 
14776  // emit specialized object double click signals:
14777  if (!candidates.isEmpty())
14778  {
14779  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(candidates.first()))
14780  {
14781  int dataIndex = 0;
14782  if (!details.first().value<QCPDataSelection>().isEmpty())
14783  dataIndex = details.first().value<QCPDataSelection>().dataRange().begin();
14784  emit plottableDoubleClick(ap, dataIndex, event);
14785  } else if (QCPAxis *ax = qobject_cast<QCPAxis*>(candidates.first()))
14786  emit axisDoubleClick(ax, details.first().value<QCPAxis::SelectablePart>(), event);
14787  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(candidates.first()))
14788  emit itemDoubleClick(ai, event);
14789  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(candidates.first()))
14790  emit legendDoubleClick(lg, 0, event);
14791  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(candidates.first()))
14792  emit legendDoubleClick(li->parentLegend(), li, event);
14793  }
14794 
14795  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14796 }
14797 
14807 void QCustomPlot::mousePressEvent(QMouseEvent *event)
14808 {
14809  emit mousePress(event);
14810  // save some state to tell in releaseEvent whether it was a click:
14811  mMouseHasMoved = false;
14812  mMousePressPos = event->pos();
14813 
14815  {
14816  if (mSelectionRectMode != QCP::srmZoom || qobject_cast<QCPAxisRect*>(axisRectAt(mMousePressPos))) // in zoom mode only activate selection rect if on an axis rect
14818  } else
14819  {
14820  // no selection rect interaction, prepare for click signal emission and forward event to layerable under the cursor:
14821  QList<QVariant> details;
14822  QList<QCPLayerable*> candidates = layerableListAt(mMousePressPos, false, &details);
14823  if (!candidates.isEmpty())
14824  {
14825  mMouseSignalLayerable = candidates.first(); // candidate for signal emission is always topmost hit layerable (signal emitted in release event)
14826  mMouseSignalLayerableDetails = details.first();
14827  }
14828  // forward event to topmost candidate which accepts the event:
14829  for (int i=0; i<candidates.size(); ++i)
14830  {
14831  event->accept(); // default impl of QCPLayerable's mouse events call ignore() on the event, in that case propagate to next candidate in list
14832  candidates.at(i)->mousePressEvent(event, details.at(i));
14833  if (event->isAccepted())
14834  {
14835  mMouseEventLayerable = candidates.at(i);
14836  mMouseEventLayerableDetails = details.at(i);
14837  break;
14838  }
14839  }
14840  }
14841 
14842  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14843 }
14844 
14857 void QCustomPlot::mouseMoveEvent(QMouseEvent *event)
14858 {
14859  emit mouseMove(event);
14860 
14861  if (!mMouseHasMoved && (mMousePressPos-event->pos()).manhattanLength() > 3)
14862  mMouseHasMoved = true; // moved too far from mouse press position, don't handle as click on mouse release
14863 
14865  mSelectionRect->moveSelection(event);
14866  else if (mMouseEventLayerable) // call event of affected layerable:
14867  mMouseEventLayerable->mouseMoveEvent(event, mMousePressPos);
14868 
14869  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14870 }
14871 
14886 void QCustomPlot::mouseReleaseEvent(QMouseEvent *event)
14887 {
14888  emit mouseRelease(event);
14889 
14890  if (!mMouseHasMoved) // mouse hasn't moved (much) between press and release, so handle as click
14891  {
14892  if (mSelectionRect && mSelectionRect->isActive()) // a simple click shouldn't successfully finish a selection rect, so cancel it here
14894  if (event->button() == Qt::LeftButton)
14895  processPointSelection(event);
14896 
14897  // emit specialized click signals of QCustomPlot instance:
14898  if (QCPAbstractPlottable *ap = qobject_cast<QCPAbstractPlottable*>(mMouseSignalLayerable))
14899  {
14900  int dataIndex = 0;
14903  emit plottableClick(ap, dataIndex, event);
14904  } else if (QCPAxis *ax = qobject_cast<QCPAxis*>(mMouseSignalLayerable))
14906  else if (QCPAbstractItem *ai = qobject_cast<QCPAbstractItem*>(mMouseSignalLayerable))
14907  emit itemClick(ai, event);
14908  else if (QCPLegend *lg = qobject_cast<QCPLegend*>(mMouseSignalLayerable))
14909  emit legendClick(lg, 0, event);
14910  else if (QCPAbstractLegendItem *li = qobject_cast<QCPAbstractLegendItem*>(mMouseSignalLayerable))
14911  emit legendClick(li->parentLegend(), li, event);
14913  }
14914 
14915  if (mSelectionRect && mSelectionRect->isActive()) // Note: if a click was detected above, the selection rect is canceled there
14916  {
14917  // finish selection rect, the appropriate action will be taken via signal-slot connection:
14918  mSelectionRect->endSelection(event);
14919  } else
14920  {
14921  // call event of affected layerable:
14923  {
14924  mMouseEventLayerable->mouseReleaseEvent(event, mMousePressPos);
14926  }
14927  }
14928 
14929  if (noAntialiasingOnDrag())
14931 
14932  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14933 }
14934 
14940 void QCustomPlot::wheelEvent(QWheelEvent *event)
14941 {
14942  emit mouseWheel(event);
14943  // forward event to layerable under cursor:
14944  QList<QCPLayerable*> candidates = layerableListAt(event->pos(), false);
14945  for (int i=0; i<candidates.size(); ++i)
14946  {
14947  event->accept(); // default impl of QCPLayerable's mouse events ignore the event, in that case propagate to next candidate in list
14948  candidates.at(i)->wheelEvent(event);
14949  if (event->isAccepted())
14950  break;
14951  }
14952  event->accept(); // in case QCPLayerable reimplementation manipulates event accepted state. In QWidget event system, QCustomPlot wants to accept the event.
14953 }
14954 
14966 {
14967  updateLayout();
14968 
14969  // draw viewport background pixmap:
14970  drawBackground(painter);
14971 
14972  // draw all layered objects (grid, axes, plottables, items, legend,...):
14973  foreach (QCPLayer *layer, mLayers)
14974  layer->draw(painter);
14975 
14976  /* Debug code to draw all layout element rects
14977  foreach (QCPLayoutElement* el, findChildren<QCPLayoutElement*>())
14978  {
14979  painter->setBrush(Qt::NoBrush);
14980  painter->setPen(QPen(QColor(0, 0, 0, 100), 0, Qt::DashLine));
14981  painter->drawRect(el->rect());
14982  painter->setPen(QPen(QColor(255, 0, 0, 100), 0, Qt::DashLine));
14983  painter->drawRect(el->outerRect());
14984  }
14985  */
14986 }
14987 
14997 {
14998  // run through layout phases:
15002 }
15003 
15022 {
15023  // Note: background color is handled in individual replot/save functions
15024 
15025  // draw background pixmap (on top of fill, if brush specified):
15026  if (!mBackgroundPixmap.isNull())
15027  {
15028  if (mBackgroundScaled)
15029  {
15030  // check whether mScaledBackground needs to be updated:
15031  QSize scaledSize(mBackgroundPixmap.size());
15032  scaledSize.scale(mViewport.size(), mBackgroundScaledMode);
15033  if (mScaledBackgroundPixmap.size() != scaledSize)
15034  mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mViewport.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
15035  painter->drawPixmap(mViewport.topLeft(), mScaledBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()) & mScaledBackgroundPixmap.rect());
15036  } else
15037  {
15038  painter->drawPixmap(mViewport.topLeft(), mBackgroundPixmap, QRect(0, 0, mViewport.width(), mViewport.height()));
15039  }
15040  }
15041 }
15042 
15063 {
15064  int bufferIndex = 0;
15065  if (mPaintBuffers.isEmpty())
15066  mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));
15067 
15068  for (int layerIndex = 0; layerIndex < mLayers.size(); ++layerIndex)
15069  {
15070  QCPLayer *layer = mLayers.at(layerIndex);
15071  if (layer->mode() == QCPLayer::lmLogical)
15072  {
15073  layer->mPaintBuffer = mPaintBuffers.at(bufferIndex).toWeakRef();
15074  } else if (layer->mode() == QCPLayer::lmBuffered)
15075  {
15076  ++bufferIndex;
15077  if (bufferIndex >= mPaintBuffers.size())
15078  mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));
15079  layer->mPaintBuffer = mPaintBuffers.at(bufferIndex).toWeakRef();
15080  if (layerIndex < mLayers.size()-1 && mLayers.at(layerIndex+1)->mode() == QCPLayer::lmLogical) // not last layer, and next one is logical, so prepare another buffer for next layerables
15081  {
15082  ++bufferIndex;
15083  if (bufferIndex >= mPaintBuffers.size())
15084  mPaintBuffers.append(QSharedPointer<QCPAbstractPaintBuffer>(createPaintBuffer()));
15085  }
15086  }
15087  }
15088  // remove unneeded buffers:
15089  while (mPaintBuffers.size()-1 > bufferIndex)
15090  mPaintBuffers.removeLast();
15091  // resize buffers to viewport size and clear contents:
15092  for (int i=0; i<mPaintBuffers.size(); ++i)
15093  {
15094  mPaintBuffers.at(i)->setSize(viewport().size()); // won't do anything if already correct size
15095  mPaintBuffers.at(i)->clear(Qt::transparent);
15096  mPaintBuffers.at(i)->setInvalidated();
15097  }
15098 }
15099 
15109 {
15110  if (mOpenGl)
15111  {
15112 #if defined(QCP_OPENGL_FBO)
15113  return new QCPPaintBufferGlFbo(viewport().size(), mBufferDevicePixelRatio, mGlContext, mGlPaintDevice);
15114 #elif defined(QCP_OPENGL_PBUFFER)
15115  return new QCPPaintBufferGlPbuffer(viewport().size(), mBufferDevicePixelRatio, mOpenGlMultisamples);
15116 #else
15117  qDebug() << Q_FUNC_INFO << "OpenGL enabled even though no support for it compiled in, this shouldn't have happened. Falling back to pixmap paint buffer.";
15119 #endif
15120  } else
15122 }
15123 
15136 {
15137  for (int i=0; i<mPaintBuffers.size(); ++i)
15138  {
15139  if (mPaintBuffers.at(i)->invalidated())
15140  return true;
15141  }
15142  return false;
15143 }
15144 
15159 {
15160 #ifdef QCP_OPENGL_FBO
15161  freeOpenGl();
15162  QSurfaceFormat proposedSurfaceFormat;
15163  proposedSurfaceFormat.setSamples(mOpenGlMultisamples);
15164 #ifdef QCP_OPENGL_OFFSCREENSURFACE
15165  QOffscreenSurface *surface = new QOffscreenSurface;
15166 #else
15167  QWindow *surface = new QWindow;
15168  surface->setSurfaceType(QSurface::OpenGLSurface);
15169 #endif
15170  surface->setFormat(proposedSurfaceFormat);
15171  surface->create();
15172  mGlSurface = QSharedPointer<QSurface>(surface);
15173  mGlContext = QSharedPointer<QOpenGLContext>(new QOpenGLContext);
15174  mGlContext->setFormat(mGlSurface->format());
15175  if (!mGlContext->create())
15176  {
15177  qDebug() << Q_FUNC_INFO << "Failed to create OpenGL context";
15178  mGlContext.clear();
15179  mGlSurface.clear();
15180  return false;
15181  }
15182  if (!mGlContext->makeCurrent(mGlSurface.data())) // context needs to be current to create paint device
15183  {
15184  qDebug() << Q_FUNC_INFO << "Failed to make opengl context current";
15185  mGlContext.clear();
15186  mGlSurface.clear();
15187  return false;
15188  }
15189  if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
15190  {
15191  qDebug() << Q_FUNC_INFO << "OpenGL of this system doesn't support frame buffer objects";
15192  mGlContext.clear();
15193  mGlSurface.clear();
15194  return false;
15195  }
15196  mGlPaintDevice = QSharedPointer<QOpenGLPaintDevice>(new QOpenGLPaintDevice);
15197  return true;
15198 #elif defined(QCP_OPENGL_PBUFFER)
15199  return QGLFormat::hasOpenGL();
15200 #else
15201  return false;
15202 #endif
15203 }
15204 
15217 {
15218 #ifdef QCP_OPENGL_FBO
15219  mGlPaintDevice.clear();
15220  mGlContext.clear();
15221  mGlSurface.clear();
15222 #endif
15223 }
15224 
15231 {
15232  if (xAxis == axis)
15233  xAxis = 0;
15234  if (xAxis2 == axis)
15235  xAxis2 = 0;
15236  if (yAxis == axis)
15237  yAxis = 0;
15238  if (yAxis2 == axis)
15239  yAxis2 = 0;
15240 
15241  // Note: No need to take care of range drag axes and range zoom axes, because they are stored in smart pointers
15242 }
15243 
15250 {
15251  if (this->legend == legend)
15252  this->legend = 0;
15253 }
15254 
15272 void QCustomPlot::processRectSelection(QRect rect, QMouseEvent *event)
15273 {
15274  bool selectionStateChanged = false;
15275 
15276  if (mInteractions.testFlag(QCP::iSelectPlottables))
15277  {
15278  QMap<int, QPair<QCPAbstractPlottable*, QCPDataSelection> > potentialSelections; // map key is number of selected data points, so we have selections sorted by size
15279  QRectF rectF(rect.normalized());
15280  if (QCPAxisRect *affectedAxisRect = axisRectAt(rectF.topLeft()))
15281  {
15282  // determine plottables that were hit by the rect and thus are candidates for selection:
15283  foreach (QCPAbstractPlottable *plottable, affectedAxisRect->plottables())
15284  {
15285  if (QCPPlottableInterface1D *plottableInterface = plottable->interface1D())
15286  {
15287  QCPDataSelection dataSel = plottableInterface->selectTestRect(rectF, true);
15288  if (!dataSel.isEmpty())
15289  potentialSelections.insertMulti(dataSel.dataPointCount(), QPair<QCPAbstractPlottable*, QCPDataSelection>(plottable, dataSel));
15290  }
15291  }
15292 
15293  if (!mInteractions.testFlag(QCP::iMultiSelect))
15294  {
15295  // only leave plottable with most selected points in map, since we will only select a single plottable:
15296  if (!potentialSelections.isEmpty())
15297  {
15298  QMap<int, QPair<QCPAbstractPlottable*, QCPDataSelection> >::iterator it = potentialSelections.begin();
15299  while (it != potentialSelections.end()-1) // erase all except last element
15300  it = potentialSelections.erase(it);
15301  }
15302  }
15303 
15304  bool additive = event->modifiers().testFlag(mMultiSelectModifier);
15305  // deselect all other layerables if not additive selection:
15306  if (!additive)
15307  {
15308  // emit deselection except to those plottables who will be selected afterwards:
15309  foreach (QCPLayer *layer, mLayers)
15310  {
15311  foreach (QCPLayerable *layerable, layer->children())
15312  {
15313  if ((potentialSelections.isEmpty() || potentialSelections.constBegin()->first != layerable) && mInteractions.testFlag(layerable->selectionCategory()))
15314  {
15315  bool selChanged = false;
15316  layerable->deselectEvent(&selChanged);
15317  selectionStateChanged |= selChanged;
15318  }
15319  }
15320  }
15321  }
15322 
15323  // go through selections in reverse (largest selection first) and emit select events:
15324  QMap<int, QPair<QCPAbstractPlottable*, QCPDataSelection> >::const_iterator it = potentialSelections.constEnd();
15325  while (it != potentialSelections.constBegin())
15326  {
15327  --it;
15328  if (mInteractions.testFlag(it.value().first->selectionCategory()))
15329  {
15330  bool selChanged = false;
15331  it.value().first->selectEvent(event, additive, QVariant::fromValue(it.value().second), &selChanged);
15332  selectionStateChanged |= selChanged;
15333  }
15334  }
15335  }
15336  }
15337 
15338  if (selectionStateChanged)
15339  {
15340  emit selectionChangedByUser();
15342  } else if (mSelectionRect)
15343  mSelectionRect->layer()->replot();
15344 }
15345 
15357 void QCustomPlot::processRectZoom(QRect rect, QMouseEvent *event)
15358 {
15359  Q_UNUSED(event)
15360  if (QCPAxisRect *axisRect = axisRectAt(rect.topLeft()))
15361  {
15362  QList<QCPAxis*> affectedAxes = QList<QCPAxis*>() << axisRect->rangeZoomAxes(Qt::Horizontal) << axisRect->rangeZoomAxes(Qt::Vertical);
15363  affectedAxes.removeAll(static_cast<QCPAxis*>(0));
15364  axisRect->zoom(QRectF(rect), affectedAxes);
15365  }
15366  replot(rpQueuedReplot); // always replot to make selection rect disappear
15367 }
15368 
15386 void QCustomPlot::processPointSelection(QMouseEvent *event)
15387 {
15388  QVariant details;
15389  QCPLayerable *clickedLayerable = layerableAt(event->pos(), true, &details);
15390  bool selectionStateChanged = false;
15391  bool additive = mInteractions.testFlag(QCP::iMultiSelect) && event->modifiers().testFlag(mMultiSelectModifier);
15392  // deselect all other layerables if not additive selection:
15393  if (!additive)
15394  {
15395  foreach (QCPLayer *layer, mLayers)
15396  {
15397  foreach (QCPLayerable *layerable, layer->children())
15398  {
15399  if (layerable != clickedLayerable && mInteractions.testFlag(layerable->selectionCategory()))
15400  {
15401  bool selChanged = false;
15402  layerable->deselectEvent(&selChanged);
15403  selectionStateChanged |= selChanged;
15404  }
15405  }
15406  }
15407  }
15408  if (clickedLayerable && mInteractions.testFlag(clickedLayerable->selectionCategory()))
15409  {
15410  // a layerable was actually clicked, call its selectEvent:
15411  bool selChanged = false;
15412  clickedLayerable->selectEvent(event, additive, details, &selChanged);
15413  selectionStateChanged |= selChanged;
15414  }
15415  if (selectionStateChanged)
15416  {
15417  emit selectionChangedByUser();
15419  }
15420 }
15421 
15434 {
15435  if (mPlottables.contains(plottable))
15436  {
15437  qDebug() << Q_FUNC_INFO << "plottable already added to this QCustomPlot:" << reinterpret_cast<quintptr>(plottable);
15438  return false;
15439  }
15440  if (plottable->parentPlot() != this)
15441  {
15442  qDebug() << Q_FUNC_INFO << "plottable not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(plottable);
15443  return false;
15444  }
15445 
15446  mPlottables.append(plottable);
15447  // possibly add plottable to legend:
15449  plottable->addToLegend();
15450  if (!plottable->layer()) // usually the layer is already set in the constructor of the plottable (via QCPLayerable constructor)
15451  plottable->setLayer(currentLayer());
15452  return true;
15453 }
15454 
15465 {
15466  if (!graph)
15467  {
15468  qDebug() << Q_FUNC_INFO << "passed graph is zero";
15469  return false;
15470  }
15471  if (mGraphs.contains(graph))
15472  {
15473  qDebug() << Q_FUNC_INFO << "graph already registered with this QCustomPlot";
15474  return false;
15475  }
15476 
15477  mGraphs.append(graph);
15478  return true;
15479 }
15480 
15481 
15492 {
15493  if (mItems.contains(item))
15494  {
15495  qDebug() << Q_FUNC_INFO << "item already added to this QCustomPlot:" << reinterpret_cast<quintptr>(item);
15496  return false;
15497  }
15498  if (item->parentPlot() != this)
15499  {
15500  qDebug() << Q_FUNC_INFO << "item not created with this QCustomPlot as parent:" << reinterpret_cast<quintptr>(item);
15501  return false;
15502  }
15503 
15504  mItems.append(item);
15505  if (!item->layer()) // usually the layer is already set in the constructor of the item (via QCPLayerable constructor)
15506  item->setLayer(currentLayer());
15507  return true;
15508 }
15509 
15517 {
15518  for (int i=0; i<mLayers.size(); ++i)
15519  mLayers.at(i)->mIndex = i;
15520 }
15521 
15537 QCPLayerable *QCustomPlot::layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails) const
15538 {
15539  QList<QVariant> details;
15540  QList<QCPLayerable*> candidates = layerableListAt(pos, onlySelectable, selectionDetails ? &details : 0);
15541  if (selectionDetails && !details.isEmpty())
15542  *selectionDetails = details.first();
15543  if (!candidates.isEmpty())
15544  return candidates.first();
15545  else
15546  return 0;
15547 }
15548 
15567 QList<QCPLayerable*> QCustomPlot::layerableListAt(const QPointF &pos, bool onlySelectable, QList<QVariant> *selectionDetails) const
15568 {
15569  QList<QCPLayerable*> result;
15570  for (int layerIndex=mLayers.size()-1; layerIndex>=0; --layerIndex)
15571  {
15572  const QList<QCPLayerable*> layerables = mLayers.at(layerIndex)->children();
15573  for (int i=layerables.size()-1; i>=0; --i)
15574  {
15575  if (!layerables.at(i)->realVisibility())
15576  continue;
15577  QVariant details;
15578  double dist = layerables.at(i)->selectTest(pos, onlySelectable, selectionDetails ? &details : 0);
15579  if (dist >= 0 && dist < selectionTolerance())
15580  {
15581  result.append(layerables.at(i));
15582  if (selectionDetails)
15583  selectionDetails->append(details);
15584  }
15585  }
15586  }
15587  return result;
15588 }
15589 
15608 bool QCustomPlot::saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality, int resolution, QCP::ResolutionUnit resolutionUnit)
15609 {
15610  QImage buffer = toPixmap(width, height, scale).toImage();
15611 
15612  int dotsPerMeter = 0;
15613  switch (resolutionUnit)
15614  {
15615  case QCP::ruDotsPerMeter: dotsPerMeter = resolution; break;
15616  case QCP::ruDotsPerCentimeter: dotsPerMeter = resolution*100; break;
15617  case QCP::ruDotsPerInch: dotsPerMeter = resolution/0.0254; break;
15618  }
15619  buffer.setDotsPerMeterX(dotsPerMeter); // this is saved together with some image formats, e.g. PNG, and is relevant when opening image in other tools
15620  buffer.setDotsPerMeterY(dotsPerMeter); // this is saved together with some image formats, e.g. PNG, and is relevant when opening image in other tools
15621  if (!buffer.isNull())
15622  return buffer.save(fileName, format, quality);
15623  else
15624  return false;
15625 }
15626 
15635 QPixmap QCustomPlot::toPixmap(int width, int height, double scale)
15636 {
15637  // this method is somewhat similar to toPainter. Change something here, and a change in toPainter might be necessary, too.
15638  int newWidth, newHeight;
15639  if (width == 0 || height == 0)
15640  {
15641  newWidth = this->width();
15642  newHeight = this->height();
15643  } else
15644  {
15645  newWidth = width;
15646  newHeight = height;
15647  }
15648  int scaledWidth = qRound(scale*newWidth);
15649  int scaledHeight = qRound(scale*newHeight);
15650 
15651  QPixmap result(scaledWidth, scaledHeight);
15652  result.fill(mBackgroundBrush.style() == Qt::SolidPattern ? mBackgroundBrush.color() : Qt::transparent); // if using non-solid pattern, make transparent now and draw brush pattern later
15653  QCPPainter painter;
15654  painter.begin(&result);
15655  if (painter.isActive())
15656  {
15657  QRect oldViewport = viewport();
15658  setViewport(QRect(0, 0, newWidth, newHeight));
15660  if (!qFuzzyCompare(scale, 1.0))
15661  {
15662  if (scale > 1.0) // for scale < 1 we always want cosmetic pens where possible, because else lines might disappear for very small scales
15664  painter.scale(scale, scale);
15665  }
15666  if (mBackgroundBrush.style() != Qt::SolidPattern && mBackgroundBrush.style() != Qt::NoBrush) // solid fills were done a few lines above with QPixmap::fill
15667  painter.fillRect(mViewport, mBackgroundBrush);
15668  draw(&painter);
15669  setViewport(oldViewport);
15670  painter.end();
15671  } else // might happen if pixmap has width or height zero
15672  {
15673  qDebug() << Q_FUNC_INFO << "Couldn't activate painter on pixmap";
15674  return QPixmap();
15675  }
15676  return result;
15677 }
15678 
15691 void QCustomPlot::toPainter(QCPPainter *painter, int width, int height)
15692 {
15693  // this method is somewhat similar to toPixmap. Change something here, and a change in toPixmap might be necessary, too.
15694  int newWidth, newHeight;
15695  if (width == 0 || height == 0)
15696  {
15697  newWidth = this->width();
15698  newHeight = this->height();
15699  } else
15700  {
15701  newWidth = width;
15702  newHeight = height;
15703  }
15704 
15705  if (painter->isActive())
15706  {
15707  QRect oldViewport = viewport();
15708  setViewport(QRect(0, 0, newWidth, newHeight));
15709  painter->setMode(QCPPainter::pmNoCaching);
15710  if (mBackgroundBrush.style() != Qt::NoBrush) // unlike in toPixmap, we can't do QPixmap::fill for Qt::SolidPattern brush style, so we also draw solid fills with fillRect here
15711  painter->fillRect(mViewport, mBackgroundBrush);
15712  draw(painter);
15713  setViewport(oldViewport);
15714  } else
15715  qDebug() << Q_FUNC_INFO << "Passed painter is not active";
15716 }
15717 /* end of 'src/core.cpp' */
15718 
15719 //amalgamation: add plottable1d.cpp
15720 
15721 /* including file 'src/colorgradient.cpp', size 24646 */
15722 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
15723 
15724 
15728 
15764  mLevelCount(350),
15765  mColorInterpolation(ciRGB),
15766  mPeriodic(false),
15767  mColorBufferInvalidated(true)
15768 {
15769  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
15770 }
15771 
15779  mLevelCount(350),
15781  mPeriodic(false),
15783 {
15784  mColorBuffer.fill(qRgb(0, 0, 0), mLevelCount);
15785  loadPreset(preset);
15786 }
15787 
15788 /* undocumented operator */
15790 {
15791  return ((other.mLevelCount == this->mLevelCount) &&
15792  (other.mColorInterpolation == this->mColorInterpolation) &&
15793  (other.mPeriodic == this->mPeriodic) &&
15794  (other.mColorStops == this->mColorStops));
15795 }
15796 
15804 {
15805  if (n < 2)
15806  {
15807  qDebug() << Q_FUNC_INFO << "n must be greater or equal 2 but was" << n;
15808  n = 2;
15809  }
15810  if (n != mLevelCount)
15811  {
15812  mLevelCount = n;
15813  mColorBufferInvalidated = true;
15814  }
15815 }
15816 
15828 void QCPColorGradient::setColorStops(const QMap<double, QColor> &colorStops)
15829 {
15831  mColorBufferInvalidated = true;
15832 }
15833 
15840 void QCPColorGradient::setColorStopAt(double position, const QColor &color)
15841 {
15842  mColorStops.insert(position, color);
15843  mColorBufferInvalidated = true;
15844 }
15845 
15854 {
15855  if (interpolation != mColorInterpolation)
15856  {
15857  mColorInterpolation = interpolation;
15858  mColorBufferInvalidated = true;
15859  }
15860 }
15861 
15878 {
15879  mPeriodic = enabled;
15880 }
15881 
15900 void QCPColorGradient::colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
15901 {
15902  // If you change something here, make sure to also adapt color() and the other colorize() overload
15903  if (!data)
15904  {
15905  qDebug() << Q_FUNC_INFO << "null pointer given as data";
15906  return;
15907  }
15908  if (!scanLine)
15909  {
15910  qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
15911  return;
15912  }
15915 
15916  if (!logarithmic)
15917  {
15918  const double posToIndexFactor = (mLevelCount-1)/range.size();
15919  if (mPeriodic)
15920  {
15921  for (int i=0; i<n; ++i)
15922  {
15923  int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
15924  if (index < 0)
15925  index += mLevelCount;
15926  scanLine[i] = mColorBuffer.at(index);
15927  }
15928  } else
15929  {
15930  for (int i=0; i<n; ++i)
15931  {
15932  int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
15933  if (index < 0)
15934  index = 0;
15935  else if (index >= mLevelCount)
15936  index = mLevelCount-1;
15937  scanLine[i] = mColorBuffer.at(index);
15938  }
15939  }
15940  } else // logarithmic == true
15941  {
15942  if (mPeriodic)
15943  {
15944  for (int i=0; i<n; ++i)
15945  {
15946  int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
15947  if (index < 0)
15948  index += mLevelCount;
15949  scanLine[i] = mColorBuffer.at(index);
15950  }
15951  } else
15952  {
15953  for (int i=0; i<n; ++i)
15954  {
15955  int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
15956  if (index < 0)
15957  index = 0;
15958  else if (index >= mLevelCount)
15959  index = mLevelCount-1;
15960  scanLine[i] = mColorBuffer.at(index);
15961  }
15962  }
15963  }
15964 }
15965 
15974 void QCPColorGradient::colorize(const double *data, const unsigned char *alpha, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor, bool logarithmic)
15975 {
15976  // If you change something here, make sure to also adapt color() and the other colorize() overload
15977  if (!data)
15978  {
15979  qDebug() << Q_FUNC_INFO << "null pointer given as data";
15980  return;
15981  }
15982  if (!alpha)
15983  {
15984  qDebug() << Q_FUNC_INFO << "null pointer given as alpha";
15985  return;
15986  }
15987  if (!scanLine)
15988  {
15989  qDebug() << Q_FUNC_INFO << "null pointer given as scanLine";
15990  return;
15991  }
15994 
15995  if (!logarithmic)
15996  {
15997  const double posToIndexFactor = (mLevelCount-1)/range.size();
15998  if (mPeriodic)
15999  {
16000  for (int i=0; i<n; ++i)
16001  {
16002  int index = (int)((data[dataIndexFactor*i]-range.lower)*posToIndexFactor) % mLevelCount;
16003  if (index < 0)
16004  index += mLevelCount;
16005  if (alpha[dataIndexFactor*i] == 255)
16006  {
16007  scanLine[i] = mColorBuffer.at(index);
16008  } else
16009  {
16010  const QRgb rgb = mColorBuffer.at(index);
16011  const float alphaF = alpha[dataIndexFactor*i]/255.0f;
16012  scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
16013  }
16014  }
16015  } else
16016  {
16017  for (int i=0; i<n; ++i)
16018  {
16019  int index = (data[dataIndexFactor*i]-range.lower)*posToIndexFactor;
16020  if (index < 0)
16021  index = 0;
16022  else if (index >= mLevelCount)
16023  index = mLevelCount-1;
16024  if (alpha[dataIndexFactor*i] == 255)
16025  {
16026  scanLine[i] = mColorBuffer.at(index);
16027  } else
16028  {
16029  const QRgb rgb = mColorBuffer.at(index);
16030  const float alphaF = alpha[dataIndexFactor*i]/255.0f;
16031  scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
16032  }
16033  }
16034  }
16035  } else // logarithmic == true
16036  {
16037  if (mPeriodic)
16038  {
16039  for (int i=0; i<n; ++i)
16040  {
16041  int index = (int)(qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1)) % mLevelCount;
16042  if (index < 0)
16043  index += mLevelCount;
16044  if (alpha[dataIndexFactor*i] == 255)
16045  {
16046  scanLine[i] = mColorBuffer.at(index);
16047  } else
16048  {
16049  const QRgb rgb = mColorBuffer.at(index);
16050  const float alphaF = alpha[dataIndexFactor*i]/255.0f;
16051  scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
16052  }
16053  }
16054  } else
16055  {
16056  for (int i=0; i<n; ++i)
16057  {
16058  int index = qLn(data[dataIndexFactor*i]/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
16059  if (index < 0)
16060  index = 0;
16061  else if (index >= mLevelCount)
16062  index = mLevelCount-1;
16063  if (alpha[dataIndexFactor*i] == 255)
16064  {
16065  scanLine[i] = mColorBuffer.at(index);
16066  } else
16067  {
16068  const QRgb rgb = mColorBuffer.at(index);
16069  const float alphaF = alpha[dataIndexFactor*i]/255.0f;
16070  scanLine[i] = qRgba(qRed(rgb)*alphaF, qGreen(rgb)*alphaF, qBlue(rgb)*alphaF, qAlpha(rgb)*alphaF);
16071  }
16072  }
16073  }
16074  }
16075 }
16076 
16089 QRgb QCPColorGradient::color(double position, const QCPRange &range, bool logarithmic)
16090 {
16091  // If you change something here, make sure to also adapt ::colorize()
16094  int index = 0;
16095  if (!logarithmic)
16096  index = (position-range.lower)*(mLevelCount-1)/range.size();
16097  else
16098  index = qLn(position/range.lower)/qLn(range.upper/range.lower)*(mLevelCount-1);
16099  if (mPeriodic)
16100  {
16101  index = index % mLevelCount;
16102  if (index < 0)
16103  index += mLevelCount;
16104  } else
16105  {
16106  if (index < 0)
16107  index = 0;
16108  else if (index >= mLevelCount)
16109  index = mLevelCount-1;
16110  }
16111  return mColorBuffer.at(index);
16112 }
16113 
16122 {
16123  clearColorStops();
16124  switch (preset)
16125  {
16126  case gpGrayscale:
16128  setColorStopAt(0, Qt::black);
16129  setColorStopAt(1, Qt::white);
16130  break;
16131  case gpHot:
16133  setColorStopAt(0, QColor(50, 0, 0));
16134  setColorStopAt(0.2, QColor(180, 10, 0));
16135  setColorStopAt(0.4, QColor(245, 50, 0));
16136  setColorStopAt(0.6, QColor(255, 150, 10));
16137  setColorStopAt(0.8, QColor(255, 255, 50));
16138  setColorStopAt(1, QColor(255, 255, 255));
16139  break;
16140  case gpCold:
16142  setColorStopAt(0, QColor(0, 0, 50));
16143  setColorStopAt(0.2, QColor(0, 10, 180));
16144  setColorStopAt(0.4, QColor(0, 50, 245));
16145  setColorStopAt(0.6, QColor(10, 150, 255));
16146  setColorStopAt(0.8, QColor(50, 255, 255));
16147  setColorStopAt(1, QColor(255, 255, 255));
16148  break;
16149  case gpNight:
16151  setColorStopAt(0, QColor(10, 20, 30));
16152  setColorStopAt(1, QColor(250, 255, 250));
16153  break;
16154  case gpCandy:
16156  setColorStopAt(0, QColor(0, 0, 255));
16157  setColorStopAt(1, QColor(255, 250, 250));
16158  break;
16159  case gpGeography:
16161  setColorStopAt(0, QColor(70, 170, 210));
16162  setColorStopAt(0.20, QColor(90, 160, 180));
16163  setColorStopAt(0.25, QColor(45, 130, 175));
16164  setColorStopAt(0.30, QColor(100, 140, 125));
16165  setColorStopAt(0.5, QColor(100, 140, 100));
16166  setColorStopAt(0.6, QColor(130, 145, 120));
16167  setColorStopAt(0.7, QColor(140, 130, 120));
16168  setColorStopAt(0.9, QColor(180, 190, 190));
16169  setColorStopAt(1, QColor(210, 210, 230));
16170  break;
16171  case gpIon:
16173  setColorStopAt(0, QColor(50, 10, 10));
16174  setColorStopAt(0.45, QColor(0, 0, 255));
16175  setColorStopAt(0.8, QColor(0, 255, 255));
16176  setColorStopAt(1, QColor(0, 255, 0));
16177  break;
16178  case gpThermal:
16180  setColorStopAt(0, QColor(0, 0, 50));
16181  setColorStopAt(0.15, QColor(20, 0, 120));
16182  setColorStopAt(0.33, QColor(200, 30, 140));
16183  setColorStopAt(0.6, QColor(255, 100, 0));
16184  setColorStopAt(0.85, QColor(255, 255, 40));
16185  setColorStopAt(1, QColor(255, 255, 255));
16186  break;
16187  case gpPolar:
16189  setColorStopAt(0, QColor(50, 255, 255));
16190  setColorStopAt(0.18, QColor(10, 70, 255));
16191  setColorStopAt(0.28, QColor(10, 10, 190));
16192  setColorStopAt(0.5, QColor(0, 0, 0));
16193  setColorStopAt(0.72, QColor(190, 10, 10));
16194  setColorStopAt(0.82, QColor(255, 70, 10));
16195  setColorStopAt(1, QColor(255, 255, 50));
16196  break;
16197  case gpSpectrum:
16199  setColorStopAt(0, QColor(50, 0, 50));
16200  setColorStopAt(0.15, QColor(0, 0, 255));
16201  setColorStopAt(0.35, QColor(0, 255, 255));
16202  setColorStopAt(0.6, QColor(255, 255, 0));
16203  setColorStopAt(0.75, QColor(255, 30, 0));
16204  setColorStopAt(1, QColor(50, 0, 0));
16205  break;
16206  case gpJet:
16208  setColorStopAt(0, QColor(0, 0, 100));
16209  setColorStopAt(0.15, QColor(0, 50, 255));
16210  setColorStopAt(0.35, QColor(0, 255, 255));
16211  setColorStopAt(0.65, QColor(255, 255, 0));
16212  setColorStopAt(0.85, QColor(255, 30, 0));
16213  setColorStopAt(1, QColor(100, 0, 0));
16214  break;
16215  case gpHues:
16217  setColorStopAt(0, QColor(255, 0, 0));
16218  setColorStopAt(1.0/3.0, QColor(0, 0, 255));
16219  setColorStopAt(2.0/3.0, QColor(0, 255, 0));
16220  setColorStopAt(1, QColor(255, 0, 0));
16221  break;
16222  }
16223 }
16224 
16231 {
16232  mColorStops.clear();
16233  mColorBufferInvalidated = true;
16234 }
16235 
16243 {
16244  QCPColorGradient result(*this);
16245  result.clearColorStops();
16246  for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
16247  result.setColorStopAt(1.0-it.key(), it.value());
16248  return result;
16249 }
16250 
16257 {
16258  for (QMap<double, QColor>::const_iterator it=mColorStops.constBegin(); it!=mColorStops.constEnd(); ++it)
16259  {
16260  if (it.value().alpha() < 255)
16261  return true;
16262  }
16263  return false;
16264 }
16265 
16272 {
16273  if (mColorBuffer.size() != mLevelCount)
16274  mColorBuffer.resize(mLevelCount);
16275  if (mColorStops.size() > 1)
16276  {
16277  double indexToPosFactor = 1.0/(double)(mLevelCount-1);
16278  const bool useAlpha = stopsUseAlpha();
16279  for (int i=0; i<mLevelCount; ++i)
16280  {
16281  double position = i*indexToPosFactor;
16282  QMap<double, QColor>::const_iterator it = mColorStops.lowerBound(position);
16283  if (it == mColorStops.constEnd()) // position is on or after last stop, use color of last stop
16284  {
16285  mColorBuffer[i] = (it-1).value().rgba();
16286  } else if (it == mColorStops.constBegin()) // position is on or before first stop, use color of first stop
16287  {
16288  mColorBuffer[i] = it.value().rgba();
16289  } else // position is in between stops (or on an intermediate stop), interpolate color
16290  {
16291  QMap<double, QColor>::const_iterator high = it;
16292  QMap<double, QColor>::const_iterator low = it-1;
16293  double t = (position-low.key())/(high.key()-low.key()); // interpolation factor 0..1
16294  switch (mColorInterpolation)
16295  {
16296  case ciRGB:
16297  {
16298  if (useAlpha)
16299  {
16300  const int alpha = (1-t)*low.value().alpha() + t*high.value().alpha();
16301  const float alphaPremultiplier = alpha/255.0f; // since we use QImage::Format_ARGB32_Premultiplied
16302  mColorBuffer[i] = qRgba(((1-t)*low.value().red() + t*high.value().red())*alphaPremultiplier,
16303  ((1-t)*low.value().green() + t*high.value().green())*alphaPremultiplier,
16304  ((1-t)*low.value().blue() + t*high.value().blue())*alphaPremultiplier,
16305  alpha);
16306  } else
16307  {
16308  mColorBuffer[i] = qRgb(((1-t)*low.value().red() + t*high.value().red()),
16309  ((1-t)*low.value().green() + t*high.value().green()),
16310  ((1-t)*low.value().blue() + t*high.value().blue()));
16311  }
16312  break;
16313  }
16314  case ciHSV:
16315  {
16316  QColor lowHsv = low.value().toHsv();
16317  QColor highHsv = high.value().toHsv();
16318  double hue = 0;
16319  double hueDiff = highHsv.hueF()-lowHsv.hueF();
16320  if (hueDiff > 0.5)
16321  hue = lowHsv.hueF() - t*(1.0-hueDiff);
16322  else if (hueDiff < -0.5)
16323  hue = lowHsv.hueF() + t*(1.0+hueDiff);
16324  else
16325  hue = lowHsv.hueF() + t*hueDiff;
16326  if (hue < 0) hue += 1.0;
16327  else if (hue >= 1.0) hue -= 1.0;
16328  if (useAlpha)
16329  {
16330  const QRgb rgb = QColor::fromHsvF(hue,
16331  (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(),
16332  (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
16333  const float alpha = (1-t)*lowHsv.alphaF() + t*highHsv.alphaF();
16334  mColorBuffer[i] = qRgba(qRed(rgb)*alpha, qGreen(rgb)*alpha, qBlue(rgb)*alpha, 255*alpha);
16335  }
16336  else
16337  {
16338  mColorBuffer[i] = QColor::fromHsvF(hue,
16339  (1-t)*lowHsv.saturationF() + t*highHsv.saturationF(),
16340  (1-t)*lowHsv.valueF() + t*highHsv.valueF()).rgb();
16341  }
16342  break;
16343  }
16344  }
16345  }
16346  }
16347  } else if (mColorStops.size() == 1)
16348  {
16349  const QRgb rgb = mColorStops.constBegin().value().rgb();
16350  const float alpha = mColorStops.constBegin().value().alphaF();
16351  mColorBuffer.fill(qRgba(qRed(rgb)*alpha, qGreen(rgb)*alpha, qBlue(rgb)*alpha, 255*alpha));
16352  } else // mColorStops is empty, fill color buffer with black
16353  {
16354  mColorBuffer.fill(qRgb(0, 0, 0));
16355  }
16356  mColorBufferInvalidated = false;
16357 }
16358 /* end of 'src/colorgradient.cpp' */
16359 
16360 
16361 /* including file 'src/selectiondecorator-bracket.cpp', size 12313 */
16362 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
16363 
16367 
16388  mBracketPen(QPen(Qt::black)),
16389  mBracketBrush(Qt::NoBrush),
16390  mBracketWidth(5),
16391  mBracketHeight(50),
16392  mBracketStyle(bsSquareBracket),
16393  mTangentToData(false),
16394  mTangentAverage(2)
16395 {
16396 
16397 }
16398 
16400 {
16401 }
16402 
16408 {
16409  mBracketPen = pen;
16410 }
16411 
16417 {
16418  mBracketBrush = brush;
16419 }
16420 
16427 {
16428  mBracketWidth = width;
16429 }
16430 
16437 {
16438  mBracketHeight = height;
16439 }
16440 
16447 {
16448  mBracketStyle = style;
16449 }
16450 
16459 {
16460  mTangentToData = enabled;
16461 }
16462 
16472 {
16473  mTangentAverage = pointCount;
16474  if (mTangentAverage < 1)
16475  mTangentAverage = 1;
16476 }
16477 
16491 void QCPSelectionDecoratorBracket::drawBracket(QCPPainter *painter, int direction) const
16492 {
16493  switch (mBracketStyle)
16494  {
16495  case bsSquareBracket:
16496  {
16497  painter->drawLine(QLineF(mBracketWidth*direction, -mBracketHeight*0.5, 0, -mBracketHeight*0.5));
16498  painter->drawLine(QLineF(mBracketWidth*direction, mBracketHeight*0.5, 0, mBracketHeight*0.5));
16499  painter->drawLine(QLineF(0, -mBracketHeight*0.5, 0, mBracketHeight*0.5));
16500  break;
16501  }
16502  case bsHalfEllipse:
16503  {
16504  painter->drawArc(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight, -90*16, -180*16*direction);
16505  break;
16506  }
16507  case bsEllipse:
16508  {
16509  painter->drawEllipse(-mBracketWidth*0.5, -mBracketHeight*0.5, mBracketWidth, mBracketHeight);
16510  break;
16511  }
16512  case bsPlus:
16513  {
16514  painter->drawLine(QLineF(0, -mBracketHeight*0.5, 0, mBracketHeight*0.5));
16515  painter->drawLine(QLineF(-mBracketWidth*0.5, 0, mBracketWidth*0.5, 0));
16516  break;
16517  }
16518  default:
16519  {
16520  qDebug() << Q_FUNC_INFO << "unknown/custom bracket style can't be handeld by default implementation:" << static_cast<int>(mBracketStyle);
16521  break;
16522  }
16523  }
16524 }
16525 
16535 {
16536  if (!mPlottable || selection.isEmpty()) return;
16537 
16538  if (QCPPlottableInterface1D *interface1d = mPlottable->interface1D())
16539  {
16540  foreach (const QCPDataRange &dataRange, selection.dataRanges())
16541  {
16542  // determine position and (if tangent mode is enabled) angle of brackets:
16543  int openBracketDir = (mPlottable->keyAxis() && !mPlottable->keyAxis()->rangeReversed()) ? 1 : -1;
16544  int closeBracketDir = -openBracketDir;
16545  QPointF openBracketPos = getPixelCoordinates(interface1d, dataRange.begin());
16546  QPointF closeBracketPos = getPixelCoordinates(interface1d, dataRange.end()-1);
16547  double openBracketAngle = 0;
16548  double closeBracketAngle = 0;
16549  if (mTangentToData)
16550  {
16551  openBracketAngle = getTangentAngle(interface1d, dataRange.begin(), openBracketDir);
16552  closeBracketAngle = getTangentAngle(interface1d, dataRange.end()-1, closeBracketDir);
16553  }
16554  // draw opening bracket:
16555  QTransform oldTransform = painter->transform();
16556  painter->setPen(mBracketPen);
16557  painter->setBrush(mBracketBrush);
16558  painter->translate(openBracketPos);
16559  painter->rotate(openBracketAngle/M_PI*180.0);
16560  drawBracket(painter, openBracketDir);
16561  painter->setTransform(oldTransform);
16562  // draw closing bracket:
16563  painter->setPen(mBracketPen);
16564  painter->setBrush(mBracketBrush);
16565  painter->translate(closeBracketPos);
16566  painter->rotate(closeBracketAngle/M_PI*180.0);
16567  drawBracket(painter, closeBracketDir);
16568  painter->setTransform(oldTransform);
16569  }
16570  }
16571 }
16572 
16586 double QCPSelectionDecoratorBracket::getTangentAngle(const QCPPlottableInterface1D *interface1d, int dataIndex, int direction) const
16587 {
16588  if (!interface1d || dataIndex < 0 || dataIndex >= interface1d->dataCount())
16589  return 0;
16590  direction = direction < 0 ? -1 : 1; // enforce direction is either -1 or 1
16591 
16592  // how many steps we can actually go from index in the given direction without exceeding data bounds:
16593  int averageCount;
16594  if (direction < 0)
16595  averageCount = qMin(mTangentAverage, dataIndex);
16596  else
16597  averageCount = qMin(mTangentAverage, interface1d->dataCount()-1-dataIndex);
16598  qDebug() << averageCount;
16599  // calculate point average of averageCount points:
16600  QVector<QPointF> points(averageCount);
16601  QPointF pointsAverage;
16602  int currentIndex = dataIndex;
16603  for (int i=0; i<averageCount; ++i)
16604  {
16605  points[i] = getPixelCoordinates(interface1d, currentIndex);
16606  pointsAverage += points[i];
16607  currentIndex += direction;
16608  }
16609  pointsAverage /= (double)averageCount;
16610 
16611  // calculate slope of linear regression through points:
16612  double numSum = 0;
16613  double denomSum = 0;
16614  for (int i=0; i<averageCount; ++i)
16615  {
16616  const double dx = points.at(i).x()-pointsAverage.x();
16617  const double dy = points.at(i).y()-pointsAverage.y();
16618  numSum += dx*dy;
16619  denomSum += dx*dx;
16620  }
16621  if (!qFuzzyIsNull(denomSum) && !qFuzzyIsNull(numSum))
16622  {
16623  return qAtan2(numSum, denomSum);
16624  } else // undetermined angle, probably mTangentAverage == 1, so using only one data point
16625  return 0;
16626 }
16627 
16633 QPointF QCPSelectionDecoratorBracket::getPixelCoordinates(const QCPPlottableInterface1D *interface1d, int dataIndex) const
16634 {
16635  QCPAxis *keyAxis = mPlottable->keyAxis();
16636  QCPAxis *valueAxis = mPlottable->valueAxis();
16637  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(0, 0); }
16638 
16639  if (keyAxis->orientation() == Qt::Horizontal)
16640  return QPointF(keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)), valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)));
16641  else
16642  return QPointF(valueAxis->coordToPixel(interface1d->dataMainValue(dataIndex)), keyAxis->coordToPixel(interface1d->dataMainKey(dataIndex)));
16643 }
16644 /* end of 'src/selectiondecorator-bracket.cpp' */
16645 
16646 
16647 /* including file 'src/layoutelements/layoutelement-axisrect.cpp', size 47584 */
16648 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
16649 
16650 
16654 
16692 /* start documentation of inline functions */
16693 
16774 /* end documentation of inline functions */
16775 
16780 QCPAxisRect::QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes) :
16781  QCPLayoutElement(parentPlot),
16782  mBackgroundBrush(Qt::NoBrush),
16783  mBackgroundScaled(true),
16784  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
16785  mInsetLayout(new QCPLayoutInset),
16786  mRangeDrag(Qt::Horizontal|Qt::Vertical),
16787  mRangeZoom(Qt::Horizontal|Qt::Vertical),
16788  mRangeZoomFactorHorz(0.85),
16789  mRangeZoomFactorVert(0.85),
16790  mDragging(false)
16791 {
16794  mInsetLayout->setParent(this);
16795 
16796  setMinimumSize(50, 50);
16797  setMinimumMargins(QMargins(15, 15, 15, 15));
16798  mAxes.insert(QCPAxis::atLeft, QList<QCPAxis*>());
16799  mAxes.insert(QCPAxis::atRight, QList<QCPAxis*>());
16800  mAxes.insert(QCPAxis::atTop, QList<QCPAxis*>());
16801  mAxes.insert(QCPAxis::atBottom, QList<QCPAxis*>());
16802 
16803  if (setupDefaultAxes)
16804  {
16805  QCPAxis *xAxis = addAxis(QCPAxis::atBottom);
16806  QCPAxis *yAxis = addAxis(QCPAxis::atLeft);
16807  QCPAxis *xAxis2 = addAxis(QCPAxis::atTop);
16808  QCPAxis *yAxis2 = addAxis(QCPAxis::atRight);
16809  setRangeDragAxes(xAxis, yAxis);
16810  setRangeZoomAxes(xAxis, yAxis);
16811  xAxis2->setVisible(false);
16812  yAxis2->setVisible(false);
16813  xAxis->grid()->setVisible(true);
16814  yAxis->grid()->setVisible(true);
16815  xAxis2->grid()->setVisible(false);
16816  yAxis2->grid()->setVisible(false);
16817  xAxis2->grid()->setZeroLinePen(Qt::NoPen);
16818  yAxis2->grid()->setZeroLinePen(Qt::NoPen);
16819  xAxis2->grid()->setVisible(false);
16820  yAxis2->grid()->setVisible(false);
16821  }
16822 }
16823 
16825 {
16826  delete mInsetLayout;
16827  mInsetLayout = 0;
16828 
16829  QList<QCPAxis*> axesList = axes();
16830  for (int i=0; i<axesList.size(); ++i)
16831  removeAxis(axesList.at(i));
16832 }
16833 
16840 {
16841  return mAxes.value(type).size();
16842 }
16843 
16850 {
16851  QList<QCPAxis*> ax(mAxes.value(type));
16852  if (index >= 0 && index < ax.size())
16853  {
16854  return ax.at(index);
16855  } else
16856  {
16857  qDebug() << Q_FUNC_INFO << "Axis index out of bounds:" << index;
16858  return 0;
16859  }
16860 }
16861 
16870 QList<QCPAxis*> QCPAxisRect::axes(QCPAxis::AxisTypes types) const
16871 {
16872  QList<QCPAxis*> result;
16873  if (types.testFlag(QCPAxis::atLeft))
16874  result << mAxes.value(QCPAxis::atLeft);
16875  if (types.testFlag(QCPAxis::atRight))
16876  result << mAxes.value(QCPAxis::atRight);
16877  if (types.testFlag(QCPAxis::atTop))
16878  result << mAxes.value(QCPAxis::atTop);
16879  if (types.testFlag(QCPAxis::atBottom))
16880  result << mAxes.value(QCPAxis::atBottom);
16881  return result;
16882 }
16883 
16888 QList<QCPAxis*> QCPAxisRect::axes() const
16889 {
16890  QList<QCPAxis*> result;
16891  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
16892  while (it.hasNext())
16893  {
16894  it.next();
16895  result << it.value();
16896  }
16897  return result;
16898 }
16899 
16921 {
16922  QCPAxis *newAxis = axis;
16923  if (!newAxis)
16924  {
16925  newAxis = new QCPAxis(this, type);
16926  } else // user provided existing axis instance, do some sanity checks
16927  {
16928  if (newAxis->axisType() != type)
16929  {
16930  qDebug() << Q_FUNC_INFO << "passed axis has different axis type than specified in type parameter";
16931  return 0;
16932  }
16933  if (newAxis->axisRect() != this)
16934  {
16935  qDebug() << Q_FUNC_INFO << "passed axis doesn't have this axis rect as parent axis rect";
16936  return 0;
16937  }
16938  if (axes().contains(newAxis))
16939  {
16940  qDebug() << Q_FUNC_INFO << "passed axis is already owned by this axis rect";
16941  return 0;
16942  }
16943  }
16944  if (mAxes[type].size() > 0) // multiple axes on one side, add half-bar axis ending to additional axes with offset
16945  {
16946  bool invert = (type == QCPAxis::atRight) || (type == QCPAxis::atBottom);
16947  newAxis->setLowerEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, !invert));
16948  newAxis->setUpperEnding(QCPLineEnding(QCPLineEnding::esHalfBar, 6, 10, invert));
16949  }
16950  mAxes[type].append(newAxis);
16951 
16952  // reset convenience axis pointers on parent QCustomPlot if they are unset:
16953  if (mParentPlot && mParentPlot->axisRectCount() > 0 && mParentPlot->axisRect(0) == this)
16954  {
16955  switch (type)
16956  {
16957  case QCPAxis::atBottom: { if (!mParentPlot->xAxis) mParentPlot->xAxis = newAxis; break; }
16958  case QCPAxis::atLeft: { if (!mParentPlot->yAxis) mParentPlot->yAxis = newAxis; break; }
16959  case QCPAxis::atTop: { if (!mParentPlot->xAxis2) mParentPlot->xAxis2 = newAxis; break; }
16960  case QCPAxis::atRight: { if (!mParentPlot->yAxis2) mParentPlot->yAxis2 = newAxis; break; }
16961  }
16962  }
16963 
16964  return newAxis;
16965 }
16966 
16975 QList<QCPAxis*> QCPAxisRect::addAxes(QCPAxis::AxisTypes types)
16976 {
16977  QList<QCPAxis*> result;
16978  if (types.testFlag(QCPAxis::atLeft))
16979  result << addAxis(QCPAxis::atLeft);
16980  if (types.testFlag(QCPAxis::atRight))
16981  result << addAxis(QCPAxis::atRight);
16982  if (types.testFlag(QCPAxis::atTop))
16983  result << addAxis(QCPAxis::atTop);
16984  if (types.testFlag(QCPAxis::atBottom))
16985  result << addAxis(QCPAxis::atBottom);
16986  return result;
16987 }
16988 
16997 {
16998  // don't access axis->axisType() to provide safety when axis is an invalid pointer, rather go through all axis containers:
16999  QHashIterator<QCPAxis::AxisType, QList<QCPAxis*> > it(mAxes);
17000  while (it.hasNext())
17001  {
17002  it.next();
17003  if (it.value().contains(axis))
17004  {
17005  if (it.value().first() == axis && it.value().size() > 1) // if removing first axis, transfer axis offset to the new first axis (which at this point is the second axis, if it exists)
17006  it.value()[1]->setOffset(axis->offset());
17007  mAxes[it.key()].removeOne(axis);
17008  if (qobject_cast<QCustomPlot*>(parentPlot())) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the axis rect is not in any layout and thus QObject-child of QCustomPlot)
17009  parentPlot()->axisRemoved(axis);
17010  delete axis;
17011  return true;
17012  }
17013  }
17014  qDebug() << Q_FUNC_INFO << "Axis isn't in axis rect:" << reinterpret_cast<quintptr>(axis);
17015  return false;
17016 }
17017 
17026 void QCPAxisRect::zoom(const QRectF &pixelRect)
17027 {
17028  zoom(pixelRect, axes());
17029 }
17030 
17039 void QCPAxisRect::zoom(const QRectF &pixelRect, const QList<QCPAxis*> &affectedAxes)
17040 {
17041  foreach (QCPAxis *axis, affectedAxes)
17042  {
17043  if (!axis)
17044  {
17045  qDebug() << Q_FUNC_INFO << "a passed axis was zero";
17046  continue;
17047  }
17048  QCPRange pixelRange;
17049  if (axis->orientation() == Qt::Horizontal)
17050  pixelRange = QCPRange(pixelRect.left(), pixelRect.right());
17051  else
17052  pixelRange = QCPRange(pixelRect.top(), pixelRect.bottom());
17053  axis->setRange(axis->pixelToCoord(pixelRange.lower), axis->pixelToCoord(pixelRange.upper));
17054  }
17055 }
17056 
17076 void QCPAxisRect::setupFullAxesBox(bool connectRanges)
17077 {
17078  QCPAxis *xAxis, *yAxis, *xAxis2, *yAxis2;
17079  if (axisCount(QCPAxis::atBottom) == 0)
17080  xAxis = addAxis(QCPAxis::atBottom);
17081  else
17082  xAxis = axis(QCPAxis::atBottom);
17083 
17084  if (axisCount(QCPAxis::atLeft) == 0)
17085  yAxis = addAxis(QCPAxis::atLeft);
17086  else
17087  yAxis = axis(QCPAxis::atLeft);
17088 
17089  if (axisCount(QCPAxis::atTop) == 0)
17090  xAxis2 = addAxis(QCPAxis::atTop);
17091  else
17092  xAxis2 = axis(QCPAxis::atTop);
17093 
17094  if (axisCount(QCPAxis::atRight) == 0)
17095  yAxis2 = addAxis(QCPAxis::atRight);
17096  else
17097  yAxis2 = axis(QCPAxis::atRight);
17098 
17099  xAxis->setVisible(true);
17100  yAxis->setVisible(true);
17101  xAxis2->setVisible(true);
17102  yAxis2->setVisible(true);
17103  xAxis2->setTickLabels(false);
17104  yAxis2->setTickLabels(false);
17105 
17106  xAxis2->setRange(xAxis->range());
17107  xAxis2->setRangeReversed(xAxis->rangeReversed());
17108  xAxis2->setScaleType(xAxis->scaleType());
17109  xAxis2->setTicks(xAxis->ticks());
17110  xAxis2->setNumberFormat(xAxis->numberFormat());
17111  xAxis2->setNumberPrecision(xAxis->numberPrecision());
17112  xAxis2->ticker()->setTickCount(xAxis->ticker()->tickCount());
17113  xAxis2->ticker()->setTickOrigin(xAxis->ticker()->tickOrigin());
17114 
17115  yAxis2->setRange(yAxis->range());
17116  yAxis2->setRangeReversed(yAxis->rangeReversed());
17117  yAxis2->setScaleType(yAxis->scaleType());
17118  yAxis2->setTicks(yAxis->ticks());
17119  yAxis2->setNumberFormat(yAxis->numberFormat());
17120  yAxis2->setNumberPrecision(yAxis->numberPrecision());
17121  yAxis2->ticker()->setTickCount(yAxis->ticker()->tickCount());
17122  yAxis2->ticker()->setTickOrigin(yAxis->ticker()->tickOrigin());
17123 
17124  if (connectRanges)
17125  {
17126  connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
17127  connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));
17128  }
17129 }
17130 
17139 QList<QCPAbstractPlottable*> QCPAxisRect::plottables() const
17140 {
17141  // Note: don't append all QCPAxis::plottables() into a list, because we might get duplicate entries
17142  QList<QCPAbstractPlottable*> result;
17143  for (int i=0; i<mParentPlot->mPlottables.size(); ++i)
17144  {
17145  if (mParentPlot->mPlottables.at(i)->keyAxis()->axisRect() == this || mParentPlot->mPlottables.at(i)->valueAxis()->axisRect() == this)
17146  result.append(mParentPlot->mPlottables.at(i));
17147  }
17148  return result;
17149 }
17150 
17159 QList<QCPGraph*> QCPAxisRect::graphs() const
17160 {
17161  // Note: don't append all QCPAxis::graphs() into a list, because we might get duplicate entries
17162  QList<QCPGraph*> result;
17163  for (int i=0; i<mParentPlot->mGraphs.size(); ++i)
17164  {
17165  if (mParentPlot->mGraphs.at(i)->keyAxis()->axisRect() == this || mParentPlot->mGraphs.at(i)->valueAxis()->axisRect() == this)
17166  result.append(mParentPlot->mGraphs.at(i));
17167  }
17168  return result;
17169 }
17170 
17181 QList<QCPAbstractItem *> QCPAxisRect::items() const
17182 {
17183  // Note: don't just append all QCPAxis::items() into a list, because we might get duplicate entries
17184  // and miss those items that have this axis rect as clipAxisRect.
17185  QList<QCPAbstractItem*> result;
17186  for (int itemId=0; itemId<mParentPlot->mItems.size(); ++itemId)
17187  {
17188  if (mParentPlot->mItems.at(itemId)->clipAxisRect() == this)
17189  {
17190  result.append(mParentPlot->mItems.at(itemId));
17191  continue;
17192  }
17193  QList<QCPItemPosition*> positions = mParentPlot->mItems.at(itemId)->positions();
17194  for (int posId=0; posId<positions.size(); ++posId)
17195  {
17196  if (positions.at(posId)->axisRect() == this ||
17197  positions.at(posId)->keyAxis()->axisRect() == this ||
17198  positions.at(posId)->valueAxis()->axisRect() == this)
17199  {
17200  result.append(mParentPlot->mItems.at(itemId));
17201  break;
17202  }
17203  }
17204  }
17205  return result;
17206 }
17207 
17219 {
17220  QCPLayoutElement::update(phase);
17221 
17222  switch (phase)
17223  {
17224  case upPreparation:
17225  {
17226  QList<QCPAxis*> allAxes = axes();
17227  for (int i=0; i<allAxes.size(); ++i)
17228  allAxes.at(i)->setupTickVectors();
17229  break;
17230  }
17231  case upLayout:
17232  {
17234  break;
17235  }
17236  default: break;
17237  }
17238 
17239  // pass update call on to inset layout (doesn't happen automatically, because QCPAxisRect doesn't derive from QCPLayout):
17240  mInsetLayout->update(phase);
17241 }
17242 
17243 /* inherits documentation from base class */
17244 QList<QCPLayoutElement*> QCPAxisRect::elements(bool recursive) const
17245 {
17246  QList<QCPLayoutElement*> result;
17247  if (mInsetLayout)
17248  {
17249  result << mInsetLayout;
17250  if (recursive)
17251  result << mInsetLayout->elements(recursive);
17252  }
17253  return result;
17254 }
17255 
17256 /* inherits documentation from base class */
17258 {
17259  painter->setAntialiasing(false);
17260 }
17261 
17262 /* inherits documentation from base class */
17264 {
17265  drawBackground(painter);
17266 }
17267 
17283 void QCPAxisRect::setBackground(const QPixmap &pm)
17284 {
17285  mBackgroundPixmap = pm;
17286  mScaledBackgroundPixmap = QPixmap();
17287 }
17288 
17302 void QCPAxisRect::setBackground(const QBrush &brush)
17303 {
17304  mBackgroundBrush = brush;
17305 }
17306 
17314 void QCPAxisRect::setBackground(const QPixmap &pm, bool scaled, Qt::AspectRatioMode mode)
17315 {
17316  mBackgroundPixmap = pm;
17317  mScaledBackgroundPixmap = QPixmap();
17318  mBackgroundScaled = scaled;
17319  mBackgroundScaledMode = mode;
17320 }
17321 
17333 {
17334  mBackgroundScaled = scaled;
17335 }
17336 
17342 void QCPAxisRect::setBackgroundScaledMode(Qt::AspectRatioMode mode)
17343 {
17344  mBackgroundScaledMode = mode;
17345 }
17346 
17353 QCPAxis *QCPAxisRect::rangeDragAxis(Qt::Orientation orientation)
17354 {
17355  if (orientation == Qt::Horizontal)
17356  return mRangeDragHorzAxis.isEmpty() ? 0 : mRangeDragHorzAxis.first().data();
17357  else
17358  return mRangeDragVertAxis.isEmpty() ? 0 : mRangeDragVertAxis.first().data();
17359 }
17360 
17367 QCPAxis *QCPAxisRect::rangeZoomAxis(Qt::Orientation orientation)
17368 {
17369  if (orientation == Qt::Horizontal)
17370  return mRangeZoomHorzAxis.isEmpty() ? 0 : mRangeZoomHorzAxis.first().data();
17371  else
17372  return mRangeZoomVertAxis.isEmpty() ? 0 : mRangeZoomVertAxis.first().data();
17373 }
17374 
17380 QList<QCPAxis*> QCPAxisRect::rangeDragAxes(Qt::Orientation orientation)
17381 {
17382  QList<QCPAxis*> result;
17383  if (orientation == Qt::Horizontal)
17384  {
17385  for (int i=0; i<mRangeDragHorzAxis.size(); ++i)
17386  {
17387  if (!mRangeDragHorzAxis.at(i).isNull())
17388  result.append(mRangeDragHorzAxis.at(i).data());
17389  }
17390  } else
17391  {
17392  for (int i=0; i<mRangeDragVertAxis.size(); ++i)
17393  {
17394  if (!mRangeDragVertAxis.at(i).isNull())
17395  result.append(mRangeDragVertAxis.at(i).data());
17396  }
17397  }
17398  return result;
17399 }
17400 
17406 QList<QCPAxis*> QCPAxisRect::rangeZoomAxes(Qt::Orientation orientation)
17407 {
17408  QList<QCPAxis*> result;
17409  if (orientation == Qt::Horizontal)
17410  {
17411  for (int i=0; i<mRangeZoomHorzAxis.size(); ++i)
17412  {
17413  if (!mRangeZoomHorzAxis.at(i).isNull())
17414  result.append(mRangeZoomHorzAxis.at(i).data());
17415  }
17416  } else
17417  {
17418  for (int i=0; i<mRangeZoomVertAxis.size(); ++i)
17419  {
17420  if (!mRangeZoomVertAxis.at(i).isNull())
17421  result.append(mRangeZoomVertAxis.at(i).data());
17422  }
17423  }
17424  return result;
17425 }
17426 
17432 double QCPAxisRect::rangeZoomFactor(Qt::Orientation orientation)
17433 {
17434  return (orientation == Qt::Horizontal ? mRangeZoomFactorHorz : mRangeZoomFactorVert);
17435 }
17436 
17453 void QCPAxisRect::setRangeDrag(Qt::Orientations orientations)
17454 {
17455  mRangeDrag = orientations;
17456 }
17457 
17473 void QCPAxisRect::setRangeZoom(Qt::Orientations orientations)
17474 {
17475  mRangeZoom = orientations;
17476 }
17477 
17488 void QCPAxisRect::setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
17489 {
17490  QList<QCPAxis*> horz, vert;
17491  if (horizontal)
17492  horz.append(horizontal);
17493  if (vertical)
17494  vert.append(vertical);
17495  setRangeDragAxes(horz, vert);
17496 }
17497 
17508 {
17509  QList<QCPAxis*> horz, vert;
17510  foreach (QCPAxis *ax, axes)
17511  {
17512  if (ax->orientation() == Qt::Horizontal)
17513  horz.append(ax);
17514  else
17515  vert.append(ax);
17516  }
17517  setRangeDragAxes(horz, vert);
17518 }
17519 
17526 void QCPAxisRect::setRangeDragAxes(QList<QCPAxis*> horizontal, QList<QCPAxis*> vertical)
17527 {
17528  mRangeDragHorzAxis.clear();
17529  foreach (QCPAxis *ax, horizontal)
17530  {
17531  QPointer<QCPAxis> axPointer(ax);
17532  if (!axPointer.isNull())
17533  mRangeDragHorzAxis.append(axPointer);
17534  else
17535  qDebug() << Q_FUNC_INFO << "invalid axis passed in horizontal list:" << reinterpret_cast<quintptr>(ax);
17536  }
17537  mRangeDragVertAxis.clear();
17538  foreach (QCPAxis *ax, vertical)
17539  {
17540  QPointer<QCPAxis> axPointer(ax);
17541  if (!axPointer.isNull())
17542  mRangeDragVertAxis.append(axPointer);
17543  else
17544  qDebug() << Q_FUNC_INFO << "invalid axis passed in vertical list:" << reinterpret_cast<quintptr>(ax);
17545  }
17546 }
17547 
17560 void QCPAxisRect::setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
17561 {
17562  QList<QCPAxis*> horz, vert;
17563  if (horizontal)
17564  horz.append(horizontal);
17565  if (vertical)
17566  vert.append(vertical);
17567  setRangeZoomAxes(horz, vert);
17568 }
17569 
17580 {
17581  QList<QCPAxis*> horz, vert;
17582  foreach (QCPAxis *ax, axes)
17583  {
17584  if (ax->orientation() == Qt::Horizontal)
17585  horz.append(ax);
17586  else
17587  vert.append(ax);
17588  }
17589  setRangeZoomAxes(horz, vert);
17590 }
17591 
17598 void QCPAxisRect::setRangeZoomAxes(QList<QCPAxis*> horizontal, QList<QCPAxis*> vertical)
17599 {
17600  mRangeZoomHorzAxis.clear();
17601  foreach (QCPAxis *ax, horizontal)
17602  {
17603  QPointer<QCPAxis> axPointer(ax);
17604  if (!axPointer.isNull())
17605  mRangeZoomHorzAxis.append(axPointer);
17606  else
17607  qDebug() << Q_FUNC_INFO << "invalid axis passed in horizontal list:" << reinterpret_cast<quintptr>(ax);
17608  }
17609  mRangeZoomVertAxis.clear();
17610  foreach (QCPAxis *ax, vertical)
17611  {
17612  QPointer<QCPAxis> axPointer(ax);
17613  if (!axPointer.isNull())
17614  mRangeZoomVertAxis.append(axPointer);
17615  else
17616  qDebug() << Q_FUNC_INFO << "invalid axis passed in vertical list:" << reinterpret_cast<quintptr>(ax);
17617  }
17618 }
17619 
17630 void QCPAxisRect::setRangeZoomFactor(double horizontalFactor, double verticalFactor)
17631 {
17632  mRangeZoomFactorHorz = horizontalFactor;
17633  mRangeZoomFactorVert = verticalFactor;
17634 }
17635 
17641 {
17642  mRangeZoomFactorHorz = factor;
17643  mRangeZoomFactorVert = factor;
17644 }
17645 
17665 {
17666  // draw background fill:
17667  if (mBackgroundBrush != Qt::NoBrush)
17668  painter->fillRect(mRect, mBackgroundBrush);
17669 
17670  // draw background pixmap (on top of fill, if brush specified):
17671  if (!mBackgroundPixmap.isNull())
17672  {
17673  if (mBackgroundScaled)
17674  {
17675  // check whether mScaledBackground needs to be updated:
17676  QSize scaledSize(mBackgroundPixmap.size());
17677  scaledSize.scale(mRect.size(), mBackgroundScaledMode);
17678  if (mScaledBackgroundPixmap.size() != scaledSize)
17679  mScaledBackgroundPixmap = mBackgroundPixmap.scaled(mRect.size(), mBackgroundScaledMode, Qt::SmoothTransformation);
17680  painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mScaledBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()) & mScaledBackgroundPixmap.rect());
17681  } else
17682  {
17683  painter->drawPixmap(mRect.topLeft()+QPoint(0, -1), mBackgroundPixmap, QRect(0, 0, mRect.width(), mRect.height()));
17684  }
17685  }
17686 }
17687 
17699 {
17700  const QList<QCPAxis*> axesList = mAxes.value(type);
17701  if (axesList.isEmpty())
17702  return;
17703 
17704  bool isFirstVisible = !axesList.first()->visible(); // if the first axis is visible, the second axis (which is where the loop starts) isn't the first visible axis, so initialize with false
17705  for (int i=1; i<axesList.size(); ++i)
17706  {
17707  int offset = axesList.at(i-1)->offset() + axesList.at(i-1)->calculateMargin();
17708  if (axesList.at(i)->visible()) // only add inner tick length to offset if this axis is visible and it's not the first visible one (might happen if true first axis is invisible)
17709  {
17710  if (!isFirstVisible)
17711  offset += axesList.at(i)->tickLengthIn();
17712  isFirstVisible = false;
17713  }
17714  axesList.at(i)->setOffset(offset);
17715  }
17716 }
17717 
17718 /* inherits documentation from base class */
17720 {
17721  if (!mAutoMargins.testFlag(side))
17722  qDebug() << Q_FUNC_INFO << "Called with side that isn't specified as auto margin";
17723 
17725 
17726  // note: only need to look at the last (outer most) axis to determine the total margin, due to updateAxisOffset call
17727  const QList<QCPAxis*> axesList = mAxes.value(QCPAxis::marginSideToAxisType(side));
17728  if (axesList.size() > 0)
17729  return axesList.last()->offset() + axesList.last()->calculateMargin();
17730  else
17731  return 0;
17732 }
17733 
17745 {
17746  if (mParentPlot && mParentPlot->axisRectCount() > 0 && mParentPlot->axisRect(0) == this)
17747  {
17756  }
17757 }
17758 
17770 void QCPAxisRect::mousePressEvent(QMouseEvent *event, const QVariant &details)
17771 {
17772  Q_UNUSED(details)
17773  if (event->buttons() & Qt::LeftButton)
17774  {
17775  mDragging = true;
17776  // initialize antialiasing backup in case we start dragging:
17778  {
17781  }
17782  // Mouse range dragging interaction:
17783  if (mParentPlot->interactions().testFlag(QCP::iRangeDrag))
17784  {
17785  mDragStartHorzRange.clear();
17786  for (int i=0; i<mRangeDragHorzAxis.size(); ++i)
17787  mDragStartHorzRange.append(mRangeDragHorzAxis.at(i).isNull() ? QCPRange() : mRangeDragHorzAxis.at(i)->range());
17788  mDragStartVertRange.clear();
17789  for (int i=0; i<mRangeDragVertAxis.size(); ++i)
17790  mDragStartVertRange.append(mRangeDragVertAxis.at(i).isNull() ? QCPRange() : mRangeDragVertAxis.at(i)->range());
17791  }
17792  }
17793 }
17794 
17802 void QCPAxisRect::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
17803 {
17804  Q_UNUSED(startPos)
17805  // Mouse range dragging interaction:
17806  if (mDragging && mParentPlot->interactions().testFlag(QCP::iRangeDrag))
17807  {
17808 
17809  if (mRangeDrag.testFlag(Qt::Horizontal))
17810  {
17811  for (int i=0; i<mRangeDragHorzAxis.size(); ++i)
17812  {
17813  QCPAxis *ax = mRangeDragHorzAxis.at(i).data();
17814  if (!ax)
17815  continue;
17816  if (i >= mDragStartHorzRange.size())
17817  break;
17818  if (ax->mScaleType == QCPAxis::stLinear)
17819  {
17820  double diff = ax->pixelToCoord(startPos.x()) - ax->pixelToCoord(event->pos().x());
17821  ax->setRange(mDragStartHorzRange.at(i).lower+diff, mDragStartHorzRange.at(i).upper+diff);
17822  } else if (ax->mScaleType == QCPAxis::stLogarithmic)
17823  {
17824  double diff = ax->pixelToCoord(startPos.x()) / ax->pixelToCoord(event->pos().x());
17825  ax->setRange(mDragStartHorzRange.at(i).lower*diff, mDragStartHorzRange.at(i).upper*diff);
17826  }
17827  }
17828  }
17829 
17830  if (mRangeDrag.testFlag(Qt::Vertical))
17831  {
17832  for (int i=0; i<mRangeDragVertAxis.size(); ++i)
17833  {
17834  QCPAxis *ax = mRangeDragVertAxis.at(i).data();
17835  if (!ax)
17836  continue;
17837  if (i >= mDragStartVertRange.size())
17838  break;
17839  if (ax->mScaleType == QCPAxis::stLinear)
17840  {
17841  double diff = ax->pixelToCoord(startPos.y()) - ax->pixelToCoord(event->pos().y());
17842  ax->setRange(mDragStartVertRange.at(i).lower+diff, mDragStartVertRange.at(i).upper+diff);
17843  } else if (ax->mScaleType == QCPAxis::stLogarithmic)
17844  {
17845  double diff = ax->pixelToCoord(startPos.y()) / ax->pixelToCoord(event->pos().y());
17846  ax->setRange(mDragStartVertRange.at(i).lower*diff, mDragStartVertRange.at(i).upper*diff);
17847  }
17848  }
17849  }
17850 
17851  if (mRangeDrag != 0) // if either vertical or horizontal drag was enabled, do a replot
17852  {
17856  }
17857 
17858  }
17859 }
17860 
17861 /* inherits documentation from base class */
17862 void QCPAxisRect::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
17863 {
17864  Q_UNUSED(event)
17865  Q_UNUSED(startPos)
17866  mDragging = false;
17868  {
17871  }
17872 }
17873 
17888 void QCPAxisRect::wheelEvent(QWheelEvent *event)
17889 {
17890  // Mouse range zooming interaction:
17891  if (mParentPlot->interactions().testFlag(QCP::iRangeZoom))
17892  {
17893  if (mRangeZoom != 0)
17894  {
17895  double factor;
17896  double wheelSteps = event->delta()/120.0; // a single step delta is +/-120 usually
17897  if (mRangeZoom.testFlag(Qt::Horizontal))
17898  {
17899  factor = qPow(mRangeZoomFactorHorz, wheelSteps);
17900  for (int i=0; i<mRangeZoomHorzAxis.size(); ++i)
17901  {
17902  if (!mRangeZoomHorzAxis.at(i).isNull())
17903  mRangeZoomHorzAxis.at(i)->scaleRange(factor, mRangeZoomHorzAxis.at(i)->pixelToCoord(event->pos().x()));
17904  }
17905  }
17906  if (mRangeZoom.testFlag(Qt::Vertical))
17907  {
17908  factor = qPow(mRangeZoomFactorVert, wheelSteps);
17909  for (int i=0; i<mRangeZoomVertAxis.size(); ++i)
17910  {
17911  if (!mRangeZoomVertAxis.at(i).isNull())
17912  mRangeZoomVertAxis.at(i)->scaleRange(factor, mRangeZoomVertAxis.at(i)->pixelToCoord(event->pos().y()));
17913  }
17914  }
17915  mParentPlot->replot();
17916  }
17917  }
17918 }
17919 /* end of 'src/layoutelements/layoutelement-axisrect.cpp' */
17920 
17921 
17922 /* including file 'src/layoutelements/layoutelement-legend.cpp', size 31097 */
17923 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
17924 
17928 
17953 /* start of documentation of signals */
17954 
17961 /* end of documentation of signals */
17962 
17968  QCPLayoutElement(parent->parentPlot()),
17969  mParentLegend(parent),
17970  mFont(parent->font()),
17971  mTextColor(parent->textColor()),
17972  mSelectedFont(parent->selectedFont()),
17973  mSelectedTextColor(parent->selectedTextColor()),
17974  mSelectable(true),
17975  mSelected(false)
17976 {
17977  setLayer(QLatin1String("legend"));
17978  setMargins(QMargins(0, 0, 0, 0));
17979 }
17980 
17987 {
17988  mFont = font;
17989 }
17990 
17996 void QCPAbstractLegendItem::setTextColor(const QColor &color)
17997 {
17998  mTextColor = color;
17999 }
18000 
18008 {
18009  mSelectedFont = font;
18010 }
18011 
18019 {
18020  mSelectedTextColor = color;
18021 }
18022 
18029 {
18030  if (mSelectable != selectable)
18031  {
18034  }
18035 }
18036 
18046 {
18047  if (mSelected != selected)
18048  {
18049  mSelected = selected;
18051  }
18052 }
18053 
18054 /* inherits documentation from base class */
18055 double QCPAbstractLegendItem::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18056 {
18057  Q_UNUSED(details)
18058  if (!mParentPlot) return -1;
18059  if (onlySelectable && (!mSelectable || !mParentLegend->selectableParts().testFlag(QCPLegend::spItems)))
18060  return -1;
18061 
18062  if (mRect.contains(pos.toPoint()))
18063  return mParentPlot->selectionTolerance()*0.99;
18064  else
18065  return -1;
18066 }
18067 
18068 /* inherits documentation from base class */
18070 {
18072 }
18073 
18074 /* inherits documentation from base class */
18076 {
18077  return mOuterRect;
18078 }
18079 
18080 /* inherits documentation from base class */
18081 void QCPAbstractLegendItem::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
18082 {
18083  Q_UNUSED(event)
18084  Q_UNUSED(details)
18086  {
18087  bool selBefore = mSelected;
18088  setSelected(additive ? !mSelected : true);
18089  if (selectionStateChanged)
18090  *selectionStateChanged = mSelected != selBefore;
18091  }
18092 }
18093 
18094 /* inherits documentation from base class */
18095 void QCPAbstractLegendItem::deselectEvent(bool *selectionStateChanged)
18096 {
18098  {
18099  bool selBefore = mSelected;
18100  setSelected(false);
18101  if (selectionStateChanged)
18102  *selectionStateChanged = mSelected != selBefore;
18103  }
18104 }
18105 
18109 
18142  QCPAbstractLegendItem(parent),
18143  mPlottable(plottable)
18144 {
18145  setAntialiased(false);
18146 }
18147 
18154 {
18156 }
18157 
18164 {
18166 }
18167 
18174 {
18175  return mSelected ? mSelectedFont : mFont;
18176 }
18177 
18185 {
18186  if (!mPlottable) return;
18187  painter->setFont(getFont());
18188  painter->setPen(QPen(getTextColor()));
18189  QSizeF iconSize = mParentLegend->iconSize();
18190  QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
18191  QRectF iconRect(mRect.topLeft(), iconSize);
18192  int textHeight = qMax(textRect.height(), iconSize.height()); // if text has smaller height than icon, center text vertically in icon height, else align tops
18193  painter->drawText(mRect.x()+iconSize.width()+mParentLegend->iconTextPadding(), mRect.y(), textRect.width(), textHeight, Qt::TextDontClip, mPlottable->name());
18194  // draw icon:
18195  painter->save();
18196  painter->setClipRect(iconRect, Qt::IntersectClip);
18197  mPlottable->drawLegendIcon(painter, iconRect);
18198  painter->restore();
18199  // draw icon border:
18200  if (getIconBorderPen().style() != Qt::NoPen)
18201  {
18202  painter->setPen(getIconBorderPen());
18203  painter->setBrush(Qt::NoBrush);
18204  int halfPen = qCeil(painter->pen().widthF()*0.5)+1;
18205  painter->setClipRect(mOuterRect.adjusted(-halfPen, -halfPen, halfPen, halfPen)); // extend default clip rect so thicker pens (especially during selection) are not clipped
18206  painter->drawRect(iconRect);
18207  }
18208 }
18209 
18218 {
18219  if (!mPlottable) return QSize();
18220  QSize result(0, 0);
18221  QRect textRect;
18222  QFontMetrics fontMetrics(getFont());
18223  QSize iconSize = mParentLegend->iconSize();
18224  textRect = fontMetrics.boundingRect(0, 0, 0, iconSize.height(), Qt::TextDontClip, mPlottable->name());
18225  result.setWidth(iconSize.width() + mParentLegend->iconTextPadding() + textRect.width());
18226  result.setHeight(qMax(textRect.height(), iconSize.height()));
18227  result.rwidth() += mMargins.left()+mMargins.right();
18228  result.rheight() += mMargins.top()+mMargins.bottom();
18229  return result;
18230 }
18231 
18232 
18236 
18271 /* start of documentation of signals */
18272 
18280 /* end of documentation of signals */
18281 
18289 {
18290  setFillOrder(QCPLayoutGrid::foRowsFirst);
18291  setWrap(0);
18292 
18293  setRowSpacing(3);
18294  setColumnSpacing(8);
18295  setMargins(QMargins(7, 5, 7, 4));
18296  setAntialiased(false);
18297  setIconSize(32, 18);
18298 
18299  setIconTextPadding(7);
18300 
18301  setSelectableParts(spLegendBox | spItems);
18302  setSelectedParts(spNone);
18303 
18304  setBorderPen(QPen(Qt::black, 0));
18305  setSelectedBorderPen(QPen(Qt::blue, 2));
18306  setIconBorderPen(Qt::NoPen);
18307  setSelectedIconBorderPen(QPen(Qt::blue, 2));
18308  setBrush(Qt::white);
18309  setSelectedBrush(Qt::white);
18310  setTextColor(Qt::black);
18311  setSelectedTextColor(Qt::blue);
18312 }
18313 
18315 {
18316  clearItems();
18317  if (qobject_cast<QCustomPlot*>(mParentPlot)) // make sure this isn't called from QObject dtor when QCustomPlot is already destructed (happens when the legend is not in any layout and thus QObject-child of QCustomPlot)
18318  mParentPlot->legendRemoved(this);
18319 }
18320 
18321 /* no doc for getter, see setSelectedParts */
18322 QCPLegend::SelectableParts QCPLegend::selectedParts() const
18323 {
18324  // check whether any legend elements selected, if yes, add spItems to return value
18325  bool hasSelectedItems = false;
18326  for (int i=0; i<itemCount(); ++i)
18327  {
18328  if (item(i) && item(i)->selected())
18329  {
18330  hasSelectedItems = true;
18331  break;
18332  }
18333  }
18334  if (hasSelectedItems)
18335  return mSelectedParts | spItems;
18336  else
18337  return mSelectedParts & ~spItems;
18338 }
18339 
18343 void QCPLegend::setBorderPen(const QPen &pen)
18344 {
18345  mBorderPen = pen;
18346 }
18347 
18351 void QCPLegend::setBrush(const QBrush &brush)
18352 {
18353  mBrush = brush;
18354 }
18355 
18365 void QCPLegend::setFont(const QFont &font)
18366 {
18367  mFont = font;
18368  for (int i=0; i<itemCount(); ++i)
18369  {
18370  if (item(i))
18371  item(i)->setFont(mFont);
18372  }
18373 }
18374 
18384 void QCPLegend::setTextColor(const QColor &color)
18385 {
18386  mTextColor = color;
18387  for (int i=0; i<itemCount(); ++i)
18388  {
18389  if (item(i))
18390  item(i)->setTextColor(color);
18391  }
18392 }
18393 
18398 void QCPLegend::setIconSize(const QSize &size)
18399 {
18400  mIconSize = size;
18401 }
18402 
18405 void QCPLegend::setIconSize(int width, int height)
18406 {
18407  mIconSize.setWidth(width);
18408  mIconSize.setHeight(height);
18409 }
18410 
18417 {
18418  mIconTextPadding = padding;
18419 }
18420 
18427 void QCPLegend::setIconBorderPen(const QPen &pen)
18428 {
18429  mIconBorderPen = pen;
18430 }
18431 
18442 void QCPLegend::setSelectableParts(const SelectableParts &selectable)
18443 {
18444  if (mSelectableParts != selectable)
18445  {
18446  mSelectableParts = selectable;
18447  emit selectableChanged(mSelectableParts);
18448  }
18449 }
18450 
18472 void QCPLegend::setSelectedParts(const SelectableParts &selected)
18473 {
18474  SelectableParts newSelected = selected;
18475  mSelectedParts = this->selectedParts(); // update mSelectedParts in case item selection changed
18476 
18477  if (mSelectedParts != newSelected)
18478  {
18479  if (!mSelectedParts.testFlag(spItems) && newSelected.testFlag(spItems)) // attempt to set spItems flag (can't do that)
18480  {
18481  qDebug() << Q_FUNC_INFO << "spItems flag can not be set, it can only be unset with this function";
18482  newSelected &= ~spItems;
18483  }
18484  if (mSelectedParts.testFlag(spItems) && !newSelected.testFlag(spItems)) // spItems flag was unset, so clear item selection
18485  {
18486  for (int i=0; i<itemCount(); ++i)
18487  {
18488  if (item(i))
18489  item(i)->setSelected(false);
18490  }
18491  }
18492  mSelectedParts = newSelected;
18493  emit selectionChanged(mSelectedParts);
18494  }
18495 }
18496 
18503 void QCPLegend::setSelectedBorderPen(const QPen &pen)
18504 {
18505  mSelectedBorderPen = pen;
18506 }
18507 
18514 {
18515  mSelectedIconBorderPen = pen;
18516 }
18517 
18524 void QCPLegend::setSelectedBrush(const QBrush &brush)
18525 {
18526  mSelectedBrush = brush;
18527 }
18528 
18537 {
18538  mSelectedFont = font;
18539  for (int i=0; i<itemCount(); ++i)
18540  {
18541  if (item(i))
18542  item(i)->setSelectedFont(font);
18543  }
18544 }
18545 
18553 void QCPLegend::setSelectedTextColor(const QColor &color)
18554 {
18555  mSelectedTextColor = color;
18556  for (int i=0; i<itemCount(); ++i)
18557  {
18558  if (item(i))
18559  item(i)->setSelectedTextColor(color);
18560  }
18561 }
18562 
18571 {
18572  return qobject_cast<QCPAbstractLegendItem*>(elementAt(index));
18573 }
18574 
18582 {
18583  for (int i=0; i<itemCount(); ++i)
18584  {
18585  if (QCPPlottableLegendItem *pli = qobject_cast<QCPPlottableLegendItem*>(item(i)))
18586  {
18587  if (pli->plottable() == plottable)
18588  return pli;
18589  }
18590  }
18591  return 0;
18592 }
18593 
18603 {
18604  return elementCount();
18605 }
18606 
18613 {
18614  for (int i=0; i<itemCount(); ++i)
18615  {
18616  if (item == this->item(i))
18617  return true;
18618  }
18619  return false;
18620 }
18621 
18629 {
18630  return itemWithPlottable(plottable);
18631 }
18632 
18644 {
18645  return addElement(item);
18646 }
18647 
18661 bool QCPLegend::removeItem(int index)
18662 {
18663  if (QCPAbstractLegendItem *ali = item(index))
18664  {
18665  bool success = remove(ali);
18666  if (success)
18667  setFillOrder(fillOrder(), true); // gets rid of empty cell by reordering
18668  return success;
18669  } else
18670  return false;
18671 }
18672 
18686 {
18687  bool success = remove(item);
18688  if (success)
18689  setFillOrder(fillOrder(), true); // gets rid of empty cell by reordering
18690  return success;
18691 }
18692 
18697 {
18698  for (int i=itemCount()-1; i>=0; --i)
18699  removeItem(i);
18700 }
18701 
18708 QList<QCPAbstractLegendItem *> QCPLegend::selectedItems() const
18709 {
18710  QList<QCPAbstractLegendItem*> result;
18711  for (int i=0; i<itemCount(); ++i)
18712  {
18713  if (QCPAbstractLegendItem *ali = item(i))
18714  {
18715  if (ali->selected())
18716  result.append(ali);
18717  }
18718  }
18719  return result;
18720 }
18721 
18738 {
18740 }
18741 
18748 {
18749  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBorderPen : mBorderPen;
18750 }
18751 
18757 QBrush QCPLegend::getBrush() const
18758 {
18759  return mSelectedParts.testFlag(spLegendBox) ? mSelectedBrush : mBrush;
18760 }
18761 
18768 {
18769  // draw background rect:
18770  painter->setBrush(getBrush());
18771  painter->setPen(getBorderPen());
18772  painter->drawRect(mOuterRect);
18773 }
18774 
18775 /* inherits documentation from base class */
18776 double QCPLegend::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
18777 {
18778  if (!mParentPlot) return -1;
18779  if (onlySelectable && !mSelectableParts.testFlag(spLegendBox))
18780  return -1;
18781 
18782  if (mOuterRect.contains(pos.toPoint()))
18783  {
18784  if (details) details->setValue(spLegendBox);
18785  return mParentPlot->selectionTolerance()*0.99;
18786  }
18787  return -1;
18788 }
18789 
18790 /* inherits documentation from base class */
18791 void QCPLegend::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
18792 {
18793  Q_UNUSED(event)
18794  mSelectedParts = selectedParts(); // in case item selection has changed
18795  if (details.value<SelectablePart>() == spLegendBox && mSelectableParts.testFlag(spLegendBox))
18796  {
18797  SelectableParts selBefore = mSelectedParts;
18798  setSelectedParts(additive ? mSelectedParts^spLegendBox : mSelectedParts|spLegendBox); // no need to unset spItems in !additive case, because they will be deselected by QCustomPlot (they're normal QCPLayerables with own deselectEvent)
18799  if (selectionStateChanged)
18800  *selectionStateChanged = mSelectedParts != selBefore;
18801  }
18802 }
18803 
18804 /* inherits documentation from base class */
18805 void QCPLegend::deselectEvent(bool *selectionStateChanged)
18806 {
18807  mSelectedParts = selectedParts(); // in case item selection has changed
18808  if (mSelectableParts.testFlag(spLegendBox))
18809  {
18810  SelectableParts selBefore = mSelectedParts;
18811  setSelectedParts(selectedParts() & ~spLegendBox);
18812  if (selectionStateChanged)
18813  *selectionStateChanged = mSelectedParts != selBefore;
18814  }
18815 }
18816 
18817 /* inherits documentation from base class */
18819 {
18820  return QCP::iSelectLegend;
18821 }
18822 
18823 /* inherits documentation from base class */
18825 {
18826  return QCP::iSelectLegend;
18827 }
18828 
18829 /* inherits documentation from base class */
18831 {
18832  if (parentPlot && !parentPlot->legend)
18833  parentPlot->legend = this;
18834 }
18835 /* end of 'src/layoutelements/layoutelement-legend.cpp' */
18836 
18837 
18838 /* including file 'src/layoutelements/layoutelement-textelement.cpp', size 12761 */
18839 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
18840 
18844 
18855 /* start documentation of signals */
18856 
18879 /* end documentation of signals */
18880 
18887  QCPLayoutElement(parentPlot),
18888  mText(),
18889  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
18890  mFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
18891  mTextColor(Qt::black),
18892  mSelectedFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
18893  mSelectedTextColor(Qt::blue),
18894  mSelectable(false),
18895  mSelected(false)
18896 {
18897  if (parentPlot)
18898  {
18899  mFont = parentPlot->font();
18900  mSelectedFont = parentPlot->font();
18901  }
18902  setMargins(QMargins(2, 2, 2, 2));
18903 }
18904 
18912  QCPLayoutElement(parentPlot),
18913  mText(text),
18914  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
18915  mFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
18916  mTextColor(Qt::black),
18917  mSelectedFont(QFont(QLatin1String("sans serif"), 12)), // will be taken from parentPlot if available, see below
18918  mSelectedTextColor(Qt::blue),
18919  mSelectable(false),
18920  mSelected(false)
18921 {
18922  if (parentPlot)
18923  {
18924  mFont = parentPlot->font();
18925  mSelectedFont = parentPlot->font();
18926  }
18927  setMargins(QMargins(2, 2, 2, 2));
18928 }
18929 
18936 QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, double pointSize) :
18937  QCPLayoutElement(parentPlot),
18938  mText(text),
18939  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
18940  mFont(QFont(QLatin1String("sans serif"), pointSize)), // will be taken from parentPlot if available, see below
18941  mTextColor(Qt::black),
18942  mSelectedFont(QFont(QLatin1String("sans serif"), pointSize)), // will be taken from parentPlot if available, see below
18943  mSelectedTextColor(Qt::blue),
18944  mSelectable(false),
18945  mSelected(false)
18946 {
18947  if (parentPlot)
18948  {
18949  mFont = parentPlot->font();
18950  mFont.setPointSizeF(pointSize);
18951  mSelectedFont = parentPlot->font();
18952  mSelectedFont.setPointSizeF(pointSize);
18953  }
18954  setMargins(QMargins(2, 2, 2, 2));
18955 }
18956 
18963 QCPTextElement::QCPTextElement(QCustomPlot *parentPlot, const QString &text, const QString &fontFamily, double pointSize) :
18964  QCPLayoutElement(parentPlot),
18965  mText(text),
18966  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
18967  mFont(QFont(fontFamily, pointSize)),
18968  mTextColor(Qt::black),
18969  mSelectedFont(QFont(fontFamily, pointSize)),
18970  mSelectedTextColor(Qt::blue),
18971  mSelectable(false),
18972  mSelected(false)
18973 {
18974  setMargins(QMargins(2, 2, 2, 2));
18975 }
18976 
18984  QCPLayoutElement(parentPlot),
18985  mText(text),
18986  mTextFlags(Qt::AlignCenter|Qt::TextWordWrap),
18987  mFont(font),
18988  mTextColor(Qt::black),
18989  mSelectedFont(font),
18990  mSelectedTextColor(Qt::blue),
18991  mSelectable(false),
18992  mSelected(false)
18993 {
18994  setMargins(QMargins(2, 2, 2, 2));
18995 }
18996 
19002 void QCPTextElement::setText(const QString &text)
19003 {
19004  mText = text;
19005 }
19006 
19028 {
19029  mTextFlags = flags;
19030 }
19031 
19037 void QCPTextElement::setFont(const QFont &font)
19038 {
19039  mFont = font;
19040 }
19041 
19047 void QCPTextElement::setTextColor(const QColor &color)
19048 {
19049  mTextColor = color;
19050 }
19051 
19058 {
19059  mSelectedFont = font;
19060 }
19061 
19067 void QCPTextElement::setSelectedTextColor(const QColor &color)
19068 {
19069  mSelectedTextColor = color;
19070 }
19071 
19079 {
19080  if (mSelectable != selectable)
19081  {
19084  }
19085 }
19086 
19095 {
19096  if (mSelected != selected)
19097  {
19098  mSelected = selected;
19100  }
19101 }
19102 
19103 /* inherits documentation from base class */
19105 {
19107 }
19108 
19109 /* inherits documentation from base class */
19111 {
19112  painter->setFont(mainFont());
19113  painter->setPen(QPen(mainTextColor()));
19114  painter->drawText(mRect, Qt::AlignCenter, mText, &mTextBoundingRect);
19115 }
19116 
19117 /* inherits documentation from base class */
19119 {
19120  QFontMetrics metrics(mFont);
19121  QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size());
19122  result.rwidth() += mMargins.left()+mMargins.right();
19123  result.rheight() += mMargins.top()+mMargins.bottom();
19124  return result;
19125 }
19126 
19127 /* inherits documentation from base class */
19129 {
19130  QFontMetrics metrics(mFont);
19131  QSize result(metrics.boundingRect(0, 0, 0, 0, Qt::AlignCenter, mText).size());
19132  result.setWidth(QWIDGETSIZE_MAX);
19133  result.rheight() += mMargins.top()+mMargins.bottom();
19134  return result;
19135 }
19136 
19137 /* inherits documentation from base class */
19138 void QCPTextElement::selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
19139 {
19140  Q_UNUSED(event)
19141  Q_UNUSED(details)
19142  if (mSelectable)
19143  {
19144  bool selBefore = mSelected;
19145  setSelected(additive ? !mSelected : true);
19146  if (selectionStateChanged)
19147  *selectionStateChanged = mSelected != selBefore;
19148  }
19149 }
19150 
19151 /* inherits documentation from base class */
19152 void QCPTextElement::deselectEvent(bool *selectionStateChanged)
19153 {
19154  if (mSelectable)
19155  {
19156  bool selBefore = mSelected;
19157  setSelected(false);
19158  if (selectionStateChanged)
19159  *selectionStateChanged = mSelected != selBefore;
19160  }
19161 }
19162 
19173 double QCPTextElement::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
19174 {
19175  Q_UNUSED(details)
19176  if (onlySelectable && !mSelectable)
19177  return -1;
19178 
19179  if (mTextBoundingRect.contains(pos.toPoint()))
19180  return mParentPlot->selectionTolerance()*0.99;
19181  else
19182  return -1;
19183 }
19184 
19191 void QCPTextElement::mousePressEvent(QMouseEvent *event, const QVariant &details)
19192 {
19193  Q_UNUSED(details)
19194  event->accept();
19195 }
19196 
19203 void QCPTextElement::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
19204 {
19205  if ((QPointF(event->pos())-startPos).manhattanLength() <= 3)
19206  emit clicked(event);
19207 }
19208 
19214 void QCPTextElement::mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)
19215 {
19216  Q_UNUSED(details)
19217  emit doubleClicked(event);
19218 }
19219 
19226 {
19227  return mSelected ? mSelectedFont : mFont;
19228 }
19229 
19236 {
19238 }
19239 /* end of 'src/layoutelements/layoutelement-textelement.cpp' */
19240 
19241 
19242 /* including file 'src/layoutelements/layoutelement-colorscale.cpp', size 25770 */
19243 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
19244 
19245 
19249 
19288 /* start documentation of inline functions */
19289 
19303 /* end documentation of signals */
19304 /* start documentation of signals */
19305 
19327 /* end documentation of signals */
19328 
19333  QCPLayoutElement(parentPlot),
19334  mType(QCPAxis::atTop), // set to atTop such that setType(QCPAxis::atRight) below doesn't skip work because it thinks it's already atRight
19335  mDataScaleType(QCPAxis::stLinear),
19336  mBarWidth(20),
19337  mAxisRect(new QCPColorScaleAxisRectPrivate(this))
19338 {
19339  setMinimumMargins(QMargins(0, 6, 0, 6)); // for default right color scale types, keep some room at bottom and top (important if no margin group is used)
19341  setDataRange(QCPRange(0, 6));
19342 }
19343 
19345 {
19346  delete mAxisRect;
19347 }
19348 
19349 /* undocumented getter */
19350 QString QCPColorScale::label() const
19351 {
19352  if (!mColorAxis)
19353  {
19354  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
19355  return QString();
19356  }
19357 
19358  return mColorAxis.data()->label();
19359 }
19360 
19361 /* undocumented getter */
19363 {
19364  if (!mAxisRect)
19365  {
19366  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19367  return false;
19368  }
19369 
19370  return mAxisRect.data()->rangeDrag().testFlag(QCPAxis::orientation(mType)) &&
19371  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType)) &&
19372  mAxisRect.data()->rangeDragAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
19373 }
19374 
19375 /* undocumented getter */
19377 {
19378  if (!mAxisRect)
19379  {
19380  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19381  return false;
19382  }
19383 
19384  return mAxisRect.data()->rangeZoom().testFlag(QCPAxis::orientation(mType)) &&
19385  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType)) &&
19386  mAxisRect.data()->rangeZoomAxis(QCPAxis::orientation(mType))->orientation() == QCPAxis::orientation(mType);
19387 }
19388 
19397 {
19398  if (!mAxisRect)
19399  {
19400  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19401  return;
19402  }
19403  if (mType != type)
19404  {
19405  mType = type;
19406  QCPRange rangeTransfer(0, 6);
19407  QString labelTransfer;
19408  QSharedPointer<QCPAxisTicker> tickerTransfer;
19409  // transfer/revert some settings on old axis if it exists:
19410  bool doTransfer = (bool)mColorAxis;
19411  if (doTransfer)
19412  {
19413  rangeTransfer = mColorAxis.data()->range();
19414  labelTransfer = mColorAxis.data()->label();
19415  tickerTransfer = mColorAxis.data()->ticker();
19416  mColorAxis.data()->setLabel(QString());
19417  disconnect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
19418  disconnect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
19419  }
19420  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atLeft << QCPAxis::atRight << QCPAxis::atBottom << QCPAxis::atTop;
19421  foreach (QCPAxis::AxisType atype, allAxisTypes)
19422  {
19423  mAxisRect.data()->axis(atype)->setTicks(atype == mType);
19424  mAxisRect.data()->axis(atype)->setTickLabels(atype== mType);
19425  }
19426  // set new mColorAxis pointer:
19427  mColorAxis = mAxisRect.data()->axis(mType);
19428  // transfer settings to new axis:
19429  if (doTransfer)
19430  {
19431  mColorAxis.data()->setRange(rangeTransfer); // range transfer necessary if axis changes from vertical to horizontal or vice versa (axes with same orientation are synchronized via signals)
19432  mColorAxis.data()->setLabel(labelTransfer);
19433  mColorAxis.data()->setTicker(tickerTransfer);
19434  }
19435  connect(mColorAxis.data(), SIGNAL(rangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
19436  connect(mColorAxis.data(), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
19437  mAxisRect.data()->setRangeDragAxes(QList<QCPAxis*>() << mColorAxis.data());
19438  }
19439 }
19440 
19451 {
19452  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
19453  {
19455  if (mColorAxis)
19456  mColorAxis.data()->setRange(mDataRange);
19458  }
19459 }
19460 
19472 {
19473  if (mDataScaleType != scaleType)
19474  {
19475  mDataScaleType = scaleType;
19476  if (mColorAxis)
19477  mColorAxis.data()->setScaleType(mDataScaleType);
19481  }
19482 }
19483 
19492 {
19493  if (mGradient != gradient)
19494  {
19495  mGradient = gradient;
19496  if (mAxisRect)
19497  mAxisRect.data()->mGradientImageInvalidated = true;
19498  emit gradientChanged(mGradient);
19499  }
19500 }
19501 
19506 void QCPColorScale::setLabel(const QString &str)
19507 {
19508  if (!mColorAxis)
19509  {
19510  qDebug() << Q_FUNC_INFO << "internal color axis undefined";
19511  return;
19512  }
19513 
19514  mColorAxis.data()->setLabel(str);
19515 }
19516 
19522 {
19523  mBarWidth = width;
19524 }
19525 
19533 {
19534  if (!mAxisRect)
19535  {
19536  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19537  return;
19538  }
19539 
19540  if (enabled)
19541  mAxisRect.data()->setRangeDrag(QCPAxis::orientation(mType));
19542  else
19543  mAxisRect.data()->setRangeDrag(0);
19544 }
19545 
19553 {
19554  if (!mAxisRect)
19555  {
19556  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19557  return;
19558  }
19559 
19560  if (enabled)
19561  mAxisRect.data()->setRangeZoom(QCPAxis::orientation(mType));
19562  else
19563  mAxisRect.data()->setRangeZoom(0);
19564 }
19565 
19569 QList<QCPColorMap*> QCPColorScale::colorMaps() const
19570 {
19571  QList<QCPColorMap*> result;
19572  for (int i=0; i<mParentPlot->plottableCount(); ++i)
19573  {
19574  if (QCPColorMap *cm = qobject_cast<QCPColorMap*>(mParentPlot->plottable(i)))
19575  if (cm->colorScale() == this)
19576  result.append(cm);
19577  }
19578  return result;
19579 }
19580 
19587 void QCPColorScale::rescaleDataRange(bool onlyVisibleMaps)
19588 {
19589  QList<QCPColorMap*> maps = colorMaps();
19590  QCPRange newRange;
19591  bool haveRange = false;
19595  for (int i=0; i<maps.size(); ++i)
19596  {
19597  if (!maps.at(i)->realVisibility() && onlyVisibleMaps)
19598  continue;
19599  QCPRange mapRange;
19600  if (maps.at(i)->colorScale() == this)
19601  {
19602  bool currentFoundRange = true;
19603  mapRange = maps.at(i)->data()->dataBounds();
19604  if (sign == QCP::sdPositive)
19605  {
19606  if (mapRange.lower <= 0 && mapRange.upper > 0)
19607  mapRange.lower = mapRange.upper*1e-3;
19608  else if (mapRange.lower <= 0 && mapRange.upper <= 0)
19609  currentFoundRange = false;
19610  } else if (sign == QCP::sdNegative)
19611  {
19612  if (mapRange.upper >= 0 && mapRange.lower < 0)
19613  mapRange.upper = mapRange.lower*1e-3;
19614  else if (mapRange.upper >= 0 && mapRange.lower >= 0)
19615  currentFoundRange = false;
19616  }
19617  if (currentFoundRange)
19618  {
19619  if (!haveRange)
19620  newRange = mapRange;
19621  else
19622  newRange.expand(mapRange);
19623  haveRange = true;
19624  }
19625  }
19626  }
19627  if (haveRange)
19628  {
19629  if (!QCPRange::validRange(newRange)) // likely due to range being zero (plottable has only constant data in this dimension), shift current range to at least center the data
19630  {
19631  double center = (newRange.lower+newRange.upper)*0.5; // upper and lower should be equal anyway, but just to make sure, incase validRange returned false for other reason
19633  {
19634  newRange.lower = center-mDataRange.size()/2.0;
19635  newRange.upper = center+mDataRange.size()/2.0;
19636  } else // mScaleType == stLogarithmic
19637  {
19638  newRange.lower = center/qSqrt(mDataRange.upper/mDataRange.lower);
19639  newRange.upper = center*qSqrt(mDataRange.upper/mDataRange.lower);
19640  }
19641  }
19642  setDataRange(newRange);
19643  }
19644 }
19645 
19646 /* inherits documentation from base class */
19648 {
19649  QCPLayoutElement::update(phase);
19650  if (!mAxisRect)
19651  {
19652  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19653  return;
19654  }
19655 
19656  mAxisRect.data()->update(phase);
19657 
19658  switch (phase)
19659  {
19660  case upMargins:
19661  {
19663  {
19664  setMaximumSize(QWIDGETSIZE_MAX, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom());
19665  setMinimumSize(0, mBarWidth+mAxisRect.data()->margins().top()+mAxisRect.data()->margins().bottom());
19666  } else
19667  {
19668  setMaximumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right(), QWIDGETSIZE_MAX);
19669  setMinimumSize(mBarWidth+mAxisRect.data()->margins().left()+mAxisRect.data()->margins().right(), 0);
19670  }
19671  break;
19672  }
19673  case upLayout:
19674  {
19675  mAxisRect.data()->setOuterRect(rect());
19676  break;
19677  }
19678  default: break;
19679  }
19680 }
19681 
19682 /* inherits documentation from base class */
19684 {
19685  painter->setAntialiasing(false);
19686 }
19687 
19688 /* inherits documentation from base class */
19689 void QCPColorScale::mousePressEvent(QMouseEvent *event, const QVariant &details)
19690 {
19691  if (!mAxisRect)
19692  {
19693  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19694  return;
19695  }
19696  mAxisRect.data()->mousePressEvent(event, details);
19697 }
19698 
19699 /* inherits documentation from base class */
19700 void QCPColorScale::mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
19701 {
19702  if (!mAxisRect)
19703  {
19704  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19705  return;
19706  }
19707  mAxisRect.data()->mouseMoveEvent(event, startPos);
19708 }
19709 
19710 /* inherits documentation from base class */
19711 void QCPColorScale::mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
19712 {
19713  if (!mAxisRect)
19714  {
19715  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19716  return;
19717  }
19718  mAxisRect.data()->mouseReleaseEvent(event, startPos);
19719 }
19720 
19721 /* inherits documentation from base class */
19722 void QCPColorScale::wheelEvent(QWheelEvent *event)
19723 {
19724  if (!mAxisRect)
19725  {
19726  qDebug() << Q_FUNC_INFO << "internal axis rect was deleted";
19727  return;
19728  }
19729  mAxisRect.data()->wheelEvent(event);
19730 }
19731 
19735 
19751  QCPAxisRect(parentColorScale->parentPlot(), true),
19752  mParentColorScale(parentColorScale),
19753  mGradientImageInvalidated(true)
19754 {
19755  setParentLayerable(parentColorScale);
19756  setMinimumMargins(QMargins(0, 0, 0, 0));
19757  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
19758  foreach (QCPAxis::AxisType type, allAxisTypes)
19759  {
19760  axis(type)->setVisible(true);
19761  axis(type)->grid()->setVisible(false);
19762  axis(type)->setPadding(0);
19763  connect(axis(type), SIGNAL(selectionChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectionChanged(QCPAxis::SelectableParts)));
19764  connect(axis(type), SIGNAL(selectableChanged(QCPAxis::SelectableParts)), this, SLOT(axisSelectableChanged(QCPAxis::SelectableParts)));
19765  }
19766 
19767  connect(axis(QCPAxis::atLeft), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atRight), SLOT(setRange(QCPRange)));
19768  connect(axis(QCPAxis::atRight), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atLeft), SLOT(setRange(QCPRange)));
19769  connect(axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atTop), SLOT(setRange(QCPRange)));
19770  connect(axis(QCPAxis::atTop), SIGNAL(rangeChanged(QCPRange)), axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
19771  connect(axis(QCPAxis::atLeft), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atRight), SLOT(setScaleType(QCPAxis::ScaleType)));
19772  connect(axis(QCPAxis::atRight), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atLeft), SLOT(setScaleType(QCPAxis::ScaleType)));
19773  connect(axis(QCPAxis::atBottom), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atTop), SLOT(setScaleType(QCPAxis::ScaleType)));
19774  connect(axis(QCPAxis::atTop), SIGNAL(scaleTypeChanged(QCPAxis::ScaleType)), axis(QCPAxis::atBottom), SLOT(setScaleType(QCPAxis::ScaleType)));
19775 
19776  // make layer transfers of color scale transfer to axis rect and axes
19777  // the axes must be set after axis rect, such that they appear above color gradient drawn by axis rect:
19778  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), this, SLOT(setLayer(QCPLayer*)));
19779  foreach (QCPAxis::AxisType type, allAxisTypes)
19780  connect(parentColorScale, SIGNAL(layerChanged(QCPLayer*)), axis(type), SLOT(setLayer(QCPLayer*)));
19781 }
19782 
19791 {
19794 
19795  bool mirrorHorz = false;
19796  bool mirrorVert = false;
19798  {
19799  mirrorHorz = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atBottom || mParentColorScale->type() == QCPAxis::atTop);
19800  mirrorVert = mParentColorScale->mColorAxis.data()->rangeReversed() && (mParentColorScale->type() == QCPAxis::atLeft || mParentColorScale->type() == QCPAxis::atRight);
19801  }
19802 
19803  painter->drawImage(rect().adjusted(0, -1, 0, -1), mGradientImage.mirrored(mirrorHorz, mirrorVert));
19804  QCPAxisRect::draw(painter);
19805 }
19806 
19813 {
19814  if (rect().isEmpty())
19815  return;
19816 
19817  const QImage::Format format = QImage::Format_ARGB32_Premultiplied;
19819  int w, h;
19820  QVector<double> data(n);
19821  for (int i=0; i<n; ++i)
19822  data[i] = i;
19824  {
19825  w = n;
19826  h = rect().height();
19827  mGradientImage = QImage(w, h, format);
19828  QVector<QRgb*> pixels;
19829  for (int y=0; y<h; ++y)
19830  pixels.append(reinterpret_cast<QRgb*>(mGradientImage.scanLine(y)));
19831  mParentColorScale->mGradient.colorize(data.constData(), QCPRange(0, n-1), pixels.first(), n);
19832  for (int y=1; y<h; ++y)
19833  memcpy(pixels.at(y), pixels.first(), n*sizeof(QRgb));
19834  } else
19835  {
19836  w = rect().width();
19837  h = n;
19838  mGradientImage = QImage(w, h, format);
19839  for (int y=0; y<h; ++y)
19840  {
19841  QRgb *pixels = reinterpret_cast<QRgb*>(mGradientImage.scanLine(y));
19842  const QRgb lineColor = mParentColorScale->mGradient.color(data[h-1-y], QCPRange(0, n-1));
19843  for (int x=0; x<w; ++x)
19844  pixels[x] = lineColor;
19845  }
19846  }
19847  mGradientImageInvalidated = false;
19848 }
19849 
19855 void QCPColorScaleAxisRectPrivate::axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
19856 {
19857  // axis bases of four axes shall always (de-)selected synchronously:
19858  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
19859  foreach (QCPAxis::AxisType type, allAxisTypes)
19860  {
19861  if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
19862  if (senderAxis->axisType() == type)
19863  continue;
19864 
19865  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
19866  {
19867  if (selectedParts.testFlag(QCPAxis::spAxis))
19868  axis(type)->setSelectedParts(axis(type)->selectedParts() | QCPAxis::spAxis);
19869  else
19870  axis(type)->setSelectedParts(axis(type)->selectedParts() & ~QCPAxis::spAxis);
19871  }
19872  }
19873 }
19874 
19880 void QCPColorScaleAxisRectPrivate::axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
19881 {
19882  // synchronize axis base selectability:
19883  QList<QCPAxis::AxisType> allAxisTypes = QList<QCPAxis::AxisType>() << QCPAxis::atBottom << QCPAxis::atTop << QCPAxis::atLeft << QCPAxis::atRight;
19884  foreach (QCPAxis::AxisType type, allAxisTypes)
19885  {
19886  if (QCPAxis *senderAxis = qobject_cast<QCPAxis*>(sender()))
19887  if (senderAxis->axisType() == type)
19888  continue;
19889 
19890  if (axis(type)->selectableParts().testFlag(QCPAxis::spAxis))
19891  {
19892  if (selectableParts.testFlag(QCPAxis::spAxis))
19893  axis(type)->setSelectableParts(axis(type)->selectableParts() | QCPAxis::spAxis);
19894  else
19895  axis(type)->setSelectableParts(axis(type)->selectableParts() & ~QCPAxis::spAxis);
19896  }
19897  }
19898 }
19899 /* end of 'src/layoutelements/layoutelement-colorscale.cpp' */
19900 
19901 
19902 /* including file 'src/plottables/plottable-graph.cpp', size 73960 */
19903 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
19904 
19908 
19923 /* start documentation of inline functions */
19924 
19974 /* end documentation of inline functions */
19975 
19980  key(0),
19981  value(0)
19982 {
19983 }
19984 
19989  key(key),
19990  value(value)
19991 {
19992 }
19993 
19994 
19998 
20037 /* start of documentation of inline functions */
20038 
20046 /* end of documentation of inline functions */
20047 
20060 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
20061  QCPAbstractPlottable1D<QCPGraphData>(keyAxis, valueAxis)
20062 {
20063  // special handling for QCPGraphs to maintain the simple graph interface:
20064  mParentPlot->registerGraph(this);
20065 
20066  setPen(QPen(Qt::blue, 0));
20067  setBrush(Qt::NoBrush);
20068 
20070  setScatterSkip(0);
20072  setAdaptiveSampling(true);
20073 }
20074 
20076 {
20077 }
20078 
20094 void QCPGraph::setData(QSharedPointer<QCPGraphDataContainer> data)
20095 {
20096  mDataContainer = data;
20097 }
20098 
20110 void QCPGraph::setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
20111 {
20112  mDataContainer->clear();
20113  addData(keys, values, alreadySorted);
20114 }
20115 
20123 {
20124  mLineStyle = ls;
20125 }
20126 
20134 {
20135  mScatterStyle = style;
20136 }
20137 
20150 {
20151  mScatterSkip = qMax(0, skip);
20152 }
20153 
20164 {
20165  // prevent setting channel target to this graph itself:
20166  if (targetGraph == this)
20167  {
20168  qDebug() << Q_FUNC_INFO << "targetGraph is this graph itself";
20169  mChannelFillGraph = 0;
20170  return;
20171  }
20172  // prevent setting channel target to a graph not in the plot:
20173  if (targetGraph && targetGraph->mParentPlot != mParentPlot)
20174  {
20175  qDebug() << Q_FUNC_INFO << "targetGraph not in same plot";
20176  mChannelFillGraph = 0;
20177  return;
20178  }
20179 
20180  mChannelFillGraph = targetGraph;
20181 }
20182 
20215 {
20216  mAdaptiveSampling = enabled;
20217 }
20218 
20231 void QCPGraph::addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
20232 {
20233  if (keys.size() != values.size())
20234  qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
20235  const int n = qMin(keys.size(), values.size());
20236  QVector<QCPGraphData> tempData(n);
20237  QVector<QCPGraphData>::iterator it = tempData.begin();
20238  const QVector<QCPGraphData>::iterator itEnd = tempData.end();
20239  int i = 0;
20240  while (it != itEnd)
20241  {
20242  it->key = keys[i];
20243  it->value = values[i];
20244  ++it;
20245  ++i;
20246  }
20247  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
20248 }
20249 
20257 void QCPGraph::addData(double key, double value)
20258 {
20259  mDataContainer->add(QCPGraphData(key, value));
20260 }
20261 
20262 /* inherits documentation from base class */
20263 double QCPGraph::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
20264 {
20265  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
20266  return -1;
20267  if (!mKeyAxis || !mValueAxis)
20268  return -1;
20269 
20270  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
20271  {
20272  QCPGraphDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
20273  double result = pointDistance(pos, closestDataPoint);
20274  if (details)
20275  {
20276  int pointIndex = closestDataPoint-mDataContainer->constBegin();
20277  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
20278  }
20279  return result;
20280  } else
20281  return -1;
20282 }
20283 
20284 /* inherits documentation from base class */
20285 QCPRange QCPGraph::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
20286 {
20287  return mDataContainer->keyRange(foundRange, inSignDomain);
20288 }
20289 
20290 /* inherits documentation from base class */
20291 QCPRange QCPGraph::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
20292 {
20293  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
20294 }
20295 
20296 /* inherits documentation from base class */
20298 {
20299  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
20300  if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return;
20301  if (mLineStyle == lsNone && mScatterStyle.isNone()) return;
20302 
20303  QVector<QPointF> lines, scatters; // line and (if necessary) scatter pixel coordinates will be stored here while iterating over segments
20304 
20305  // loop over and draw segments of unselected/selected data:
20306  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
20307  getDataSegments(selectedSegments, unselectedSegments);
20308  allSegments << unselectedSegments << selectedSegments;
20309  for (int i=0; i<allSegments.size(); ++i)
20310  {
20311  bool isSelectedSegment = i >= unselectedSegments.size();
20312  // get line pixel points appropriate to line style:
20313  QCPDataRange lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1); // unselected segments extend lines to bordering selected data point (safe to exceed total data bounds in first/last segment, getLines takes care)
20314  getLines(&lines, lineDataRange);
20315 
20316  // check data validity if flag set:
20317 #ifdef QCUSTOMPLOT_CHECK_DATA
20319  for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
20320  {
20321  if (QCP::isInvalidData(it->key, it->value))
20322  qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name();
20323  }
20324 #endif
20325 
20326  // draw fill of graph:
20327  if (isSelectedSegment && mSelectionDecorator)
20328  mSelectionDecorator->applyBrush(painter);
20329  else
20330  painter->setBrush(mBrush);
20331  painter->setPen(Qt::NoPen);
20332  drawFill(painter, &lines);
20333 
20334  // draw line:
20335  if (mLineStyle != lsNone)
20336  {
20337  if (isSelectedSegment && mSelectionDecorator)
20338  mSelectionDecorator->applyPen(painter);
20339  else
20340  painter->setPen(mPen);
20341  painter->setBrush(Qt::NoBrush);
20342  if (mLineStyle == lsImpulse)
20343  drawImpulsePlot(painter, lines);
20344  else
20345  drawLinePlot(painter, lines); // also step plots can be drawn as a line plot
20346  }
20347 
20348  // draw scatters:
20349  QCPScatterStyle finalScatterStyle = mScatterStyle;
20350  if (isSelectedSegment && mSelectionDecorator)
20352  if (!finalScatterStyle.isNone())
20353  {
20354  getScatters(&scatters, allSegments.at(i));
20355  drawScatterPlot(painter, scatters, finalScatterStyle);
20356  }
20357  }
20358 
20359  // draw other selection decoration that isn't just line/scatter pens and brushes:
20360  if (mSelectionDecorator)
20362 }
20363 
20364 /* inherits documentation from base class */
20365 void QCPGraph::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
20366 {
20367  // draw fill:
20368  if (mBrush.style() != Qt::NoBrush)
20369  {
20370  applyFillAntialiasingHint(painter);
20371  painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
20372  }
20373  // draw line vertically centered:
20374  if (mLineStyle != lsNone)
20375  {
20377  painter->setPen(mPen);
20378  painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
20379  }
20380  // draw scatter symbol:
20381  if (!mScatterStyle.isNone())
20382  {
20384  // scale scatter pixmap if it's too large to fit in legend icon rect:
20385  if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
20386  {
20387  QCPScatterStyle scaledStyle(mScatterStyle);
20388  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
20389  scaledStyle.applyTo(painter, mPen);
20390  scaledStyle.drawShape(painter, QRectF(rect).center());
20391  } else
20392  {
20393  mScatterStyle.applyTo(painter, mPen);
20394  mScatterStyle.drawShape(painter, QRectF(rect).center());
20395  }
20396  }
20397 }
20398 
20419 void QCPGraph::getLines(QVector<QPointF> *lines, const QCPDataRange &dataRange) const
20420 {
20421  if (!lines) return;
20423  getVisibleDataBounds(begin, end, dataRange);
20424  if (begin == end)
20425  {
20426  lines->clear();
20427  return;
20428  }
20429 
20430  QVector<QCPGraphData> lineData;
20431  if (mLineStyle != lsNone)
20432  getOptimizedLineData(&lineData, begin, end);
20433 
20434  if (mKeyAxis->rangeReversed() != (mKeyAxis->orientation() == Qt::Vertical)) // make sure key pixels are sorted ascending in lineData (significantly simplifies following processing)
20435  std::reverse(lineData.begin(), lineData.end());
20436 
20437  switch (mLineStyle)
20438  {
20439  case lsNone: lines->clear(); break;
20440  case lsLine: *lines = dataToLines(lineData); break;
20441  case lsStepLeft: *lines = dataToStepLeftLines(lineData); break;
20442  case lsStepRight: *lines = dataToStepRightLines(lineData); break;
20443  case lsStepCenter: *lines = dataToStepCenterLines(lineData); break;
20444  case lsImpulse: *lines = dataToImpulseLines(lineData); break;
20445  }
20446 }
20447 
20460 void QCPGraph::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange) const
20461 {
20462  if (!scatters) return;
20463  QCPAxis *keyAxis = mKeyAxis.data();
20464  QCPAxis *valueAxis = mValueAxis.data();
20465  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; scatters->clear(); return; }
20466 
20468  getVisibleDataBounds(begin, end, dataRange);
20469  if (begin == end)
20470  {
20471  scatters->clear();
20472  return;
20473  }
20474 
20475  QVector<QCPGraphData> data;
20476  getOptimizedScatterData(&data, begin, end);
20477 
20478  if (mKeyAxis->rangeReversed() != (mKeyAxis->orientation() == Qt::Vertical)) // make sure key pixels are sorted ascending in data (significantly simplifies following processing)
20479  std::reverse(data.begin(), data.end());
20480 
20481  scatters->resize(data.size());
20482  if (keyAxis->orientation() == Qt::Vertical)
20483  {
20484  for (int i=0; i<data.size(); ++i)
20485  {
20486  if (!qIsNaN(data.at(i).value))
20487  {
20488  (*scatters)[i].setX(valueAxis->coordToPixel(data.at(i).value));
20489  (*scatters)[i].setY(keyAxis->coordToPixel(data.at(i).key));
20490  }
20491  }
20492  } else
20493  {
20494  for (int i=0; i<data.size(); ++i)
20495  {
20496  if (!qIsNaN(data.at(i).value))
20497  {
20498  (*scatters)[i].setX(keyAxis->coordToPixel(data.at(i).key));
20499  (*scatters)[i].setY(valueAxis->coordToPixel(data.at(i).value));
20500  }
20501  }
20502  }
20503 }
20504 
20515 QVector<QPointF> QCPGraph::dataToLines(const QVector<QCPGraphData> &data) const
20516 {
20517  QVector<QPointF> result;
20518  QCPAxis *keyAxis = mKeyAxis.data();
20519  QCPAxis *valueAxis = mValueAxis.data();
20520  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20521 
20522  result.resize(data.size());
20523 
20524  // transform data points to pixels:
20525  if (keyAxis->orientation() == Qt::Vertical)
20526  {
20527  for (int i=0; i<data.size(); ++i)
20528  {
20529  result[i].setX(valueAxis->coordToPixel(data.at(i).value));
20530  result[i].setY(keyAxis->coordToPixel(data.at(i).key));
20531  }
20532  } else // key axis is horizontal
20533  {
20534  for (int i=0; i<data.size(); ++i)
20535  {
20536  result[i].setX(keyAxis->coordToPixel(data.at(i).key));
20537  result[i].setY(valueAxis->coordToPixel(data.at(i).value));
20538  }
20539  }
20540  return result;
20541 }
20542 
20553 QVector<QPointF> QCPGraph::dataToStepLeftLines(const QVector<QCPGraphData> &data) const
20554 {
20555  QVector<QPointF> result;
20556  QCPAxis *keyAxis = mKeyAxis.data();
20557  QCPAxis *valueAxis = mValueAxis.data();
20558  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20559 
20560  result.resize(data.size()*2);
20561 
20562  // calculate steps from data and transform to pixel coordinates:
20563  if (keyAxis->orientation() == Qt::Vertical)
20564  {
20565  double lastValue = valueAxis->coordToPixel(data.first().value);
20566  for (int i=0; i<data.size(); ++i)
20567  {
20568  const double key = keyAxis->coordToPixel(data.at(i).key);
20569  result[i*2+0].setX(lastValue);
20570  result[i*2+0].setY(key);
20571  lastValue = valueAxis->coordToPixel(data.at(i).value);
20572  result[i*2+1].setX(lastValue);
20573  result[i*2+1].setY(key);
20574  }
20575  } else // key axis is horizontal
20576  {
20577  double lastValue = valueAxis->coordToPixel(data.first().value);
20578  for (int i=0; i<data.size(); ++i)
20579  {
20580  const double key = keyAxis->coordToPixel(data.at(i).key);
20581  result[i*2+0].setX(key);
20582  result[i*2+0].setY(lastValue);
20583  lastValue = valueAxis->coordToPixel(data.at(i).value);
20584  result[i*2+1].setX(key);
20585  result[i*2+1].setY(lastValue);
20586  }
20587  }
20588  return result;
20589 }
20590 
20601 QVector<QPointF> QCPGraph::dataToStepRightLines(const QVector<QCPGraphData> &data) const
20602 {
20603  QVector<QPointF> result;
20604  QCPAxis *keyAxis = mKeyAxis.data();
20605  QCPAxis *valueAxis = mValueAxis.data();
20606  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20607 
20608  result.resize(data.size()*2);
20609 
20610  // calculate steps from data and transform to pixel coordinates:
20611  if (keyAxis->orientation() == Qt::Vertical)
20612  {
20613  double lastKey = keyAxis->coordToPixel(data.first().key);
20614  for (int i=0; i<data.size(); ++i)
20615  {
20616  const double value = valueAxis->coordToPixel(data.at(i).value);
20617  result[i*2+0].setX(value);
20618  result[i*2+0].setY(lastKey);
20619  lastKey = keyAxis->coordToPixel(data.at(i).key);
20620  result[i*2+1].setX(value);
20621  result[i*2+1].setY(lastKey);
20622  }
20623  } else // key axis is horizontal
20624  {
20625  double lastKey = keyAxis->coordToPixel(data.first().key);
20626  for (int i=0; i<data.size(); ++i)
20627  {
20628  const double value = valueAxis->coordToPixel(data.at(i).value);
20629  result[i*2+0].setX(lastKey);
20630  result[i*2+0].setY(value);
20631  lastKey = keyAxis->coordToPixel(data.at(i).key);
20632  result[i*2+1].setX(lastKey);
20633  result[i*2+1].setY(value);
20634  }
20635  }
20636  return result;
20637 }
20638 
20649 QVector<QPointF> QCPGraph::dataToStepCenterLines(const QVector<QCPGraphData> &data) const
20650 {
20651  QVector<QPointF> result;
20652  QCPAxis *keyAxis = mKeyAxis.data();
20653  QCPAxis *valueAxis = mValueAxis.data();
20654  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20655 
20656  result.resize(data.size()*2);
20657 
20658  // calculate steps from data and transform to pixel coordinates:
20659  if (keyAxis->orientation() == Qt::Vertical)
20660  {
20661  double lastKey = keyAxis->coordToPixel(data.first().key);
20662  double lastValue = valueAxis->coordToPixel(data.first().value);
20663  result[0].setX(lastValue);
20664  result[0].setY(lastKey);
20665  for (int i=1; i<data.size(); ++i)
20666  {
20667  const double key = (keyAxis->coordToPixel(data.at(i).key)+lastKey)*0.5;
20668  result[i*2-1].setX(lastValue);
20669  result[i*2-1].setY(key);
20670  lastValue = valueAxis->coordToPixel(data.at(i).value);
20671  lastKey = keyAxis->coordToPixel(data.at(i).key);
20672  result[i*2+0].setX(lastValue);
20673  result[i*2+0].setY(key);
20674  }
20675  result[data.size()*2-1].setX(lastValue);
20676  result[data.size()*2-1].setY(lastKey);
20677  } else // key axis is horizontal
20678  {
20679  double lastKey = keyAxis->coordToPixel(data.first().key);
20680  double lastValue = valueAxis->coordToPixel(data.first().value);
20681  result[0].setX(lastKey);
20682  result[0].setY(lastValue);
20683  for (int i=1; i<data.size(); ++i)
20684  {
20685  const double key = (keyAxis->coordToPixel(data.at(i).key)+lastKey)*0.5;
20686  result[i*2-1].setX(key);
20687  result[i*2-1].setY(lastValue);
20688  lastValue = valueAxis->coordToPixel(data.at(i).value);
20689  lastKey = keyAxis->coordToPixel(data.at(i).key);
20690  result[i*2+0].setX(key);
20691  result[i*2+0].setY(lastValue);
20692  }
20693  result[data.size()*2-1].setX(lastKey);
20694  result[data.size()*2-1].setY(lastValue);
20695  }
20696  return result;
20697 }
20698 
20709 QVector<QPointF> QCPGraph::dataToImpulseLines(const QVector<QCPGraphData> &data) const
20710 {
20711  QVector<QPointF> result;
20712  QCPAxis *keyAxis = mKeyAxis.data();
20713  QCPAxis *valueAxis = mValueAxis.data();
20714  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return result; }
20715 
20716  result.resize(data.size()*2);
20717 
20718  // transform data points to pixels:
20719  if (keyAxis->orientation() == Qt::Vertical)
20720  {
20721  for (int i=0; i<data.size(); ++i)
20722  {
20723  const double key = keyAxis->coordToPixel(data.at(i).key);
20724  result[i*2+0].setX(valueAxis->coordToPixel(0));
20725  result[i*2+0].setY(key);
20726  result[i*2+1].setX(valueAxis->coordToPixel(data.at(i).value));
20727  result[i*2+1].setY(key);
20728  }
20729  } else // key axis is horizontal
20730  {
20731  for (int i=0; i<data.size(); ++i)
20732  {
20733  const double key = keyAxis->coordToPixel(data.at(i).key);
20734  result[i*2+0].setX(key);
20735  result[i*2+0].setY(valueAxis->coordToPixel(0));
20736  result[i*2+1].setX(key);
20737  result[i*2+1].setY(valueAxis->coordToPixel(data.at(i).value));
20738  }
20739  }
20740  return result;
20741 }
20742 
20760 void QCPGraph::drawFill(QCPPainter *painter, QVector<QPointF> *lines) const
20761 {
20762  if (mLineStyle == lsImpulse) return; // fill doesn't make sense for impulse plot
20763  if (painter->brush().style() == Qt::NoBrush || painter->brush().color().alpha() == 0) return;
20764 
20765  applyFillAntialiasingHint(painter);
20766  QVector<QCPDataRange> segments = getNonNanSegments(lines, keyAxis()->orientation());
20767  if (!mChannelFillGraph)
20768  {
20769  // draw base fill under graph, fill goes all the way to the zero-value-line:
20770  for (int i=0; i<segments.size(); ++i)
20771  painter->drawPolygon(getFillPolygon(lines, segments.at(i)));
20772  } else
20773  {
20774  // draw fill between this graph and mChannelFillGraph:
20775  QVector<QPointF> otherLines;
20776  mChannelFillGraph->getLines(&otherLines, QCPDataRange(0, mChannelFillGraph->dataCount()));
20777  if (!otherLines.isEmpty())
20778  {
20779  QVector<QCPDataRange> otherSegments = getNonNanSegments(&otherLines, mChannelFillGraph->keyAxis()->orientation());
20780  QVector<QPair<QCPDataRange, QCPDataRange> > segmentPairs = getOverlappingSegments(segments, lines, otherSegments, &otherLines);
20781  for (int i=0; i<segmentPairs.size(); ++i)
20782  painter->drawPolygon(getChannelFillPolygon(lines, segmentPairs.at(i).first, &otherLines, segmentPairs.at(i).second));
20783  }
20784  }
20785 }
20786 
20794 void QCPGraph::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &scatters, const QCPScatterStyle &style) const
20795 {
20797  style.applyTo(painter, mPen);
20798  for (int i=0; i<scatters.size(); ++i)
20799  style.drawShape(painter, scatters.at(i).x(), scatters.at(i).y());
20800 }
20801 
20808 void QCPGraph::drawLinePlot(QCPPainter *painter, const QVector<QPointF> &lines) const
20809 {
20810  if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
20811  {
20813  drawPolyline(painter, lines);
20814  }
20815 }
20816 
20825 void QCPGraph::drawImpulsePlot(QCPPainter *painter, const QVector<QPointF> &lines) const
20826 {
20827  if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
20828  {
20830  QPen oldPen = painter->pen();
20831  QPen newPen = painter->pen();
20832  newPen.setCapStyle(Qt::FlatCap); // so impulse line doesn't reach beyond zero-line
20833  painter->setPen(newPen);
20834  painter->drawLines(lines);
20835  painter->setPen(oldPen);
20836  }
20837 }
20838 
20851 void QCPGraph::getOptimizedLineData(QVector<QCPGraphData> *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const
20852 {
20853  if (!lineData) return;
20854  QCPAxis *keyAxis = mKeyAxis.data();
20855  QCPAxis *valueAxis = mValueAxis.data();
20856  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
20857  if (begin == end) return;
20858 
20859  int dataCount = end-begin;
20860  int maxCount = std::numeric_limits<int>::max();
20861  if (mAdaptiveSampling)
20862  {
20863  double keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key));
20864  if (2*keyPixelSpan+2 < (double)std::numeric_limits<int>::max())
20865  maxCount = 2*keyPixelSpan+2;
20866  }
20867 
20868  if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
20869  {
20871  double minValue = it->value;
20872  double maxValue = it->value;
20873  QCPGraphDataContainer::const_iterator currentIntervalFirstPoint = it;
20874  int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction
20875  int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
20876  double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(begin->key)+reversedRound));
20877  double lastIntervalEndKey = currentIntervalStartKey;
20878  double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
20879  bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
20880  int intervalDataCount = 1;
20881  ++it; // advance iterator to second data point because adaptive sampling works in 1 point retrospect
20882  while (it != end)
20883  {
20884  if (it->key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this cluster if necessary
20885  {
20886  if (it->value < minValue)
20887  minValue = it->value;
20888  else if (it->value > maxValue)
20889  maxValue = it->value;
20890  ++intervalDataCount;
20891  } else // new pixel interval started
20892  {
20893  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
20894  {
20895  if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point is further away, so first point of this cluster must be at a real data point
20896  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint->value));
20897  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
20898  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
20899  if (it->key > currentIntervalStartKey+keyEpsilon*2) // new pixel started further away from previous cluster, so make sure the last point of the cluster is at a real data point
20900  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.8, (it-1)->value));
20901  } else
20902  lineData->append(QCPGraphData(currentIntervalFirstPoint->key, currentIntervalFirstPoint->value));
20903  lastIntervalEndKey = (it-1)->key;
20904  minValue = it->value;
20905  maxValue = it->value;
20906  currentIntervalFirstPoint = it;
20907  currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it->key)+reversedRound));
20908  if (keyEpsilonVariable)
20909  keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
20910  intervalDataCount = 1;
20911  }
20912  ++it;
20913  }
20914  // handle last interval:
20915  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them to a cluster
20916  {
20917  if (lastIntervalEndKey < currentIntervalStartKey-keyEpsilon) // last point wasn't a cluster, so first point of this cluster must be at a real data point
20918  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.2, currentIntervalFirstPoint->value));
20919  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.25, minValue));
20920  lineData->append(QCPGraphData(currentIntervalStartKey+keyEpsilon*0.75, maxValue));
20921  } else
20922  lineData->append(QCPGraphData(currentIntervalFirstPoint->key, currentIntervalFirstPoint->value));
20923 
20924  } else // don't use adaptive sampling algorithm, transfer points one-to-one from the data container into the output
20925  {
20926  lineData->resize(dataCount);
20927  std::copy(begin, end, lineData->begin());
20928  }
20929 }
20930 
20944 {
20945  if (!scatterData) return;
20946  QCPAxis *keyAxis = mKeyAxis.data();
20947  QCPAxis *valueAxis = mValueAxis.data();
20948  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
20949 
20950  const int scatterModulo = mScatterSkip+1;
20951  const bool doScatterSkip = mScatterSkip > 0;
20952  int beginIndex = begin-mDataContainer->constBegin();
20953  int endIndex = end-mDataContainer->constBegin();
20954  while (doScatterSkip && begin != end && beginIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter
20955  {
20956  ++beginIndex;
20957  ++begin;
20958  }
20959  if (begin == end) return;
20960  int dataCount = end-begin;
20961  int maxCount = std::numeric_limits<int>::max();
20962  if (mAdaptiveSampling)
20963  {
20964  int keyPixelSpan = qAbs(keyAxis->coordToPixel(begin->key)-keyAxis->coordToPixel((end-1)->key));
20965  maxCount = 2*keyPixelSpan+2;
20966  }
20967 
20968  if (mAdaptiveSampling && dataCount >= maxCount) // use adaptive sampling only if there are at least two points per pixel on average
20969  {
20970  double valueMaxRange = valueAxis->range().upper;
20971  double valueMinRange = valueAxis->range().lower;
20973  int itIndex = beginIndex;
20974  double minValue = it->value;
20975  double maxValue = it->value;
20976  QCPGraphDataContainer::const_iterator minValueIt = it;
20977  QCPGraphDataContainer::const_iterator maxValueIt = it;
20978  QCPGraphDataContainer::const_iterator currentIntervalStart = it;
20979  int reversedFactor = keyAxis->pixelOrientation(); // is used to calculate keyEpsilon pixel into the correct direction
20980  int reversedRound = reversedFactor==-1 ? 1 : 0; // is used to switch between floor (normal) and ceil (reversed) rounding of currentIntervalStartKey
20981  double currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(begin->key)+reversedRound));
20982  double keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor)); // interval of one pixel on screen when mapped to plot key coordinates
20983  bool keyEpsilonVariable = keyAxis->scaleType() == QCPAxis::stLogarithmic; // indicates whether keyEpsilon needs to be updated after every interval (for log axes)
20984  int intervalDataCount = 1;
20985  // advance iterator to second (non-skipped) data point because adaptive sampling works in 1 point retrospect:
20986  if (!doScatterSkip)
20987  ++it;
20988  else
20989  {
20990  itIndex += scatterModulo;
20991  if (itIndex < endIndex) // make sure we didn't jump over end
20992  it += scatterModulo;
20993  else
20994  {
20995  it = end;
20996  itIndex = endIndex;
20997  }
20998  }
20999  // main loop over data points:
21000  while (it != end)
21001  {
21002  if (it->key < currentIntervalStartKey+keyEpsilon) // data point is still within same pixel, so skip it and expand value span of this pixel if necessary
21003  {
21004  if (it->value < minValue && it->value > valueMinRange && it->value < valueMaxRange)
21005  {
21006  minValue = it->value;
21007  minValueIt = it;
21008  } else if (it->value > maxValue && it->value > valueMinRange && it->value < valueMaxRange)
21009  {
21010  maxValue = it->value;
21011  maxValueIt = it;
21012  }
21013  ++intervalDataCount;
21014  } else // new pixel started
21015  {
21016  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
21017  {
21018  // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
21019  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
21020  int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
21021  QCPGraphDataContainer::const_iterator intervalIt = currentIntervalStart;
21022  int c = 0;
21023  while (intervalIt != it)
21024  {
21025  if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt->value > valueMinRange && intervalIt->value < valueMaxRange)
21026  scatterData->append(*intervalIt);
21027  ++c;
21028  if (!doScatterSkip)
21029  ++intervalIt;
21030  else
21031  intervalIt += scatterModulo; // since we know indices of "currentIntervalStart", "intervalIt" and "it" are multiples of scatterModulo, we can't accidentally jump over "it" here
21032  }
21033  } else if (currentIntervalStart->value > valueMinRange && currentIntervalStart->value < valueMaxRange)
21034  scatterData->append(*currentIntervalStart);
21035  minValue = it->value;
21036  maxValue = it->value;
21037  currentIntervalStart = it;
21038  currentIntervalStartKey = keyAxis->pixelToCoord((int)(keyAxis->coordToPixel(it->key)+reversedRound));
21039  if (keyEpsilonVariable)
21040  keyEpsilon = qAbs(currentIntervalStartKey-keyAxis->pixelToCoord(keyAxis->coordToPixel(currentIntervalStartKey)+1.0*reversedFactor));
21041  intervalDataCount = 1;
21042  }
21043  // advance to next data point:
21044  if (!doScatterSkip)
21045  ++it;
21046  else
21047  {
21048  itIndex += scatterModulo;
21049  if (itIndex < endIndex) // make sure we didn't jump over end
21050  it += scatterModulo;
21051  else
21052  {
21053  it = end;
21054  itIndex = endIndex;
21055  }
21056  }
21057  }
21058  // handle last interval:
21059  if (intervalDataCount >= 2) // last pixel had multiple data points, consolidate them
21060  {
21061  // determine value pixel span and add as many points in interval to maintain certain vertical data density (this is specific to scatter plot):
21062  double valuePixelSpan = qAbs(valueAxis->coordToPixel(minValue)-valueAxis->coordToPixel(maxValue));
21063  int dataModulo = qMax(1, qRound(intervalDataCount/(valuePixelSpan/4.0))); // approximately every 4 value pixels one data point on average
21064  QCPGraphDataContainer::const_iterator intervalIt = currentIntervalStart;
21065  int intervalItIndex = intervalIt-mDataContainer->constBegin();
21066  int c = 0;
21067  while (intervalIt != it)
21068  {
21069  if ((c % dataModulo == 0 || intervalIt == minValueIt || intervalIt == maxValueIt) && intervalIt->value > valueMinRange && intervalIt->value < valueMaxRange)
21070  scatterData->append(*intervalIt);
21071  ++c;
21072  if (!doScatterSkip)
21073  ++intervalIt;
21074  else // here we can't guarantee that adding scatterModulo doesn't exceed "it" (because "it" is equal to "end" here, and "end" isn't scatterModulo-aligned), so check via index comparison:
21075  {
21076  intervalItIndex += scatterModulo;
21077  if (intervalItIndex < itIndex)
21078  intervalIt += scatterModulo;
21079  else
21080  {
21081  intervalIt = it;
21082  intervalItIndex = itIndex;
21083  }
21084  }
21085  }
21086  } else if (currentIntervalStart->value > valueMinRange && currentIntervalStart->value < valueMaxRange)
21087  scatterData->append(*currentIntervalStart);
21088 
21089  } else // don't use adaptive sampling algorithm, transfer points one-to-one from the data container into the output
21090  {
21092  int itIndex = beginIndex;
21093  scatterData->reserve(dataCount);
21094  while (it != end)
21095  {
21096  scatterData->append(*it);
21097  // advance to next data point:
21098  if (!doScatterSkip)
21099  ++it;
21100  else
21101  {
21102  itIndex += scatterModulo;
21103  if (itIndex < endIndex)
21104  it += scatterModulo;
21105  else
21106  {
21107  it = end;
21108  itIndex = endIndex;
21109  }
21110  }
21111  }
21112  }
21113 }
21114 
21125 {
21126  if (rangeRestriction.isEmpty())
21127  {
21128  end = mDataContainer->constEnd();
21129  begin = end;
21130  } else
21131  {
21132  QCPAxis *keyAxis = mKeyAxis.data();
21133  QCPAxis *valueAxis = mValueAxis.data();
21134  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
21135  // get visible data range:
21136  begin = mDataContainer->findBegin(keyAxis->range().lower);
21137  end = mDataContainer->findEnd(keyAxis->range().upper);
21138  // limit lower/upperEnd to rangeRestriction:
21139  mDataContainer->limitIteratorsToDataRange(begin, end, rangeRestriction); // this also ensures rangeRestriction outside data bounds doesn't break anything
21140  }
21141 }
21142 
21154 QVector<QCPDataRange> QCPGraph::getNonNanSegments(const QVector<QPointF> *lineData, Qt::Orientation keyOrientation) const
21155 {
21156  QVector<QCPDataRange> result;
21157  const int n = lineData->size();
21158 
21159  QCPDataRange currentSegment(-1, -1);
21160  int i = 0;
21161 
21162  if (keyOrientation == Qt::Horizontal)
21163  {
21164  while (i < n)
21165  {
21166  while (i < n && qIsNaN(lineData->at(i).y())) // seek next non-NaN data point
21167  ++i;
21168  if (i == n)
21169  break;
21170  currentSegment.setBegin(i++);
21171  while (i < n && !qIsNaN(lineData->at(i).y())) // seek next NaN data point or end of data
21172  ++i;
21173  currentSegment.setEnd(i++);
21174  result.append(currentSegment);
21175  }
21176  } else // keyOrientation == Qt::Vertical
21177  {
21178  while (i < n)
21179  {
21180  while (i < n && qIsNaN(lineData->at(i).x())) // seek next non-NaN data point
21181  ++i;
21182  if (i == n)
21183  break;
21184  currentSegment.setBegin(i++);
21185  while (i < n && !qIsNaN(lineData->at(i).x())) // seek next NaN data point or end of data
21186  ++i;
21187  currentSegment.setEnd(i++);
21188  result.append(currentSegment);
21189  }
21190  }
21191  return result;
21192 }
21193 
21212 QVector<QPair<QCPDataRange, QCPDataRange> > QCPGraph::getOverlappingSegments(QVector<QCPDataRange> thisSegments, const QVector<QPointF> *thisData, QVector<QCPDataRange> otherSegments, const QVector<QPointF> *otherData) const
21213 {
21214  QVector<QPair<QCPDataRange, QCPDataRange> > result;
21215  if (thisData->isEmpty() || otherData->isEmpty() || thisSegments.isEmpty() || otherSegments.isEmpty())
21216  return result;
21217 
21218  int thisIndex = 0;
21219  int otherIndex = 0;
21220  const bool verticalKey = mKeyAxis->orientation() == Qt::Vertical;
21221  while (thisIndex < thisSegments.size() && otherIndex < otherSegments.size())
21222  {
21223  if (thisSegments.at(thisIndex).size() < 2) // segments with fewer than two points won't have a fill anyhow
21224  {
21225  ++thisIndex;
21226  continue;
21227  }
21228  if (otherSegments.at(otherIndex).size() < 2) // segments with fewer than two points won't have a fill anyhow
21229  {
21230  ++otherIndex;
21231  continue;
21232  }
21233  double thisLower, thisUpper, otherLower, otherUpper;
21234  if (!verticalKey)
21235  {
21236  thisLower = thisData->at(thisSegments.at(thisIndex).begin()).x();
21237  thisUpper = thisData->at(thisSegments.at(thisIndex).end()-1).x();
21238  otherLower = otherData->at(otherSegments.at(otherIndex).begin()).x();
21239  otherUpper = otherData->at(otherSegments.at(otherIndex).end()-1).x();
21240  } else
21241  {
21242  thisLower = thisData->at(thisSegments.at(thisIndex).begin()).y();
21243  thisUpper = thisData->at(thisSegments.at(thisIndex).end()-1).y();
21244  otherLower = otherData->at(otherSegments.at(otherIndex).begin()).y();
21245  otherUpper = otherData->at(otherSegments.at(otherIndex).end()-1).y();
21246  }
21247 
21248  int bPrecedence;
21249  if (segmentsIntersect(thisLower, thisUpper, otherLower, otherUpper, bPrecedence))
21250  result.append(QPair<QCPDataRange, QCPDataRange>(thisSegments.at(thisIndex), otherSegments.at(otherIndex)));
21251 
21252  if (bPrecedence <= 0) // otherSegment doesn't reach as far as thisSegment, so continue with next otherSegment, keeping current thisSegment
21253  ++otherIndex;
21254  else // otherSegment reaches further than thisSegment, so continue with next thisSegment, keeping current otherSegment
21255  ++thisIndex;
21256  }
21257 
21258  return result;
21259 }
21260 
21275 bool QCPGraph::segmentsIntersect(double aLower, double aUpper, double bLower, double bUpper, int &bPrecedence) const
21276 {
21277  bPrecedence = 0;
21278  if (aLower > bUpper)
21279  {
21280  bPrecedence = -1;
21281  return false;
21282  } else if (bLower > aUpper)
21283  {
21284  bPrecedence = 1;
21285  return false;
21286  } else
21287  {
21288  if (aUpper > bUpper)
21289  bPrecedence = -1;
21290  else if (aUpper < bUpper)
21291  bPrecedence = 1;
21292 
21293  return true;
21294  }
21295 }
21296 
21308 QPointF QCPGraph::getFillBasePoint(QPointF matchingDataPoint) const
21309 {
21310  QCPAxis *keyAxis = mKeyAxis.data();
21311  QCPAxis *valueAxis = mValueAxis.data();
21312  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
21313 
21314  QPointF result;
21315  if (valueAxis->scaleType() == QCPAxis::stLinear)
21316  {
21317  if (keyAxis->orientation() == Qt::Horizontal)
21318  {
21319  result.setX(matchingDataPoint.x());
21320  result.setY(valueAxis->coordToPixel(0));
21321  } else // keyAxis->orientation() == Qt::Vertical
21322  {
21323  result.setX(valueAxis->coordToPixel(0));
21324  result.setY(matchingDataPoint.y());
21325  }
21326  } else // valueAxis->mScaleType == QCPAxis::stLogarithmic
21327  {
21328  // In logarithmic scaling we can't just draw to value 0 so we just fill all the way
21329  // to the axis which is in the direction towards 0
21330  if (keyAxis->orientation() == Qt::Vertical)
21331  {
21332  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
21333  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
21334  result.setX(keyAxis->axisRect()->right());
21335  else
21336  result.setX(keyAxis->axisRect()->left());
21337  result.setY(matchingDataPoint.y());
21338  } else if (keyAxis->axisType() == QCPAxis::atTop || keyAxis->axisType() == QCPAxis::atBottom)
21339  {
21340  result.setX(matchingDataPoint.x());
21341  if ((valueAxis->range().upper < 0 && !valueAxis->rangeReversed()) ||
21342  (valueAxis->range().upper > 0 && valueAxis->rangeReversed())) // if range is negative, zero is on opposite side of key axis
21343  result.setY(keyAxis->axisRect()->top());
21344  else
21345  result.setY(keyAxis->axisRect()->bottom());
21346  }
21347  }
21348  return result;
21349 }
21350 
21367 const QPolygonF QCPGraph::getFillPolygon(const QVector<QPointF> *lineData, QCPDataRange segment) const
21368 {
21369  if (segment.size() < 2)
21370  return QPolygonF();
21371  QPolygonF result(segment.size()+2);
21372 
21373  result[0] = getFillBasePoint(lineData->at(segment.begin()));
21374  std::copy(lineData->constBegin()+segment.begin(), lineData->constBegin()+segment.end(), result.begin()+1);
21375  result[result.size()-1] = getFillBasePoint(lineData->at(segment.end()-1));
21376 
21377  return result;
21378 }
21379 
21398 const QPolygonF QCPGraph::getChannelFillPolygon(const QVector<QPointF> *thisData, QCPDataRange thisSegment, const QVector<QPointF> *otherData, QCPDataRange otherSegment) const
21399 {
21400  if (!mChannelFillGraph)
21401  return QPolygonF();
21402 
21403  QCPAxis *keyAxis = mKeyAxis.data();
21404  QCPAxis *valueAxis = mValueAxis.data();
21405  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPolygonF(); }
21406  if (!mChannelFillGraph.data()->mKeyAxis) { qDebug() << Q_FUNC_INFO << "channel fill target key axis invalid"; return QPolygonF(); }
21407 
21408  if (mChannelFillGraph.data()->mKeyAxis.data()->orientation() != keyAxis->orientation())
21409  return QPolygonF(); // don't have same axis orientation, can't fill that (Note: if keyAxis fits, valueAxis will fit too, because it's always orthogonal to keyAxis)
21410 
21411  if (thisData->isEmpty()) return QPolygonF();
21412  QVector<QPointF> thisSegmentData(thisSegment.size());
21413  QVector<QPointF> otherSegmentData(otherSegment.size());
21414  std::copy(thisData->constBegin()+thisSegment.begin(), thisData->constBegin()+thisSegment.end(), thisSegmentData.begin());
21415  std::copy(otherData->constBegin()+otherSegment.begin(), otherData->constBegin()+otherSegment.end(), otherSegmentData.begin());
21416  // pointers to be able to swap them, depending which data range needs cropping:
21417  QVector<QPointF> *staticData = &thisSegmentData;
21418  QVector<QPointF> *croppedData = &otherSegmentData;
21419 
21420  // crop both vectors to ranges in which the keys overlap (which coord is key, depends on axisType):
21421  if (keyAxis->orientation() == Qt::Horizontal)
21422  {
21423  // x is key
21424  // crop lower bound:
21425  if (staticData->first().x() < croppedData->first().x()) // other one must be cropped
21426  qSwap(staticData, croppedData);
21427  const int lowBound = findIndexBelowX(croppedData, staticData->first().x());
21428  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
21429  croppedData->remove(0, lowBound);
21430  // set lowest point of cropped data to fit exactly key position of first static data point via linear interpolation:
21431  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
21432  double slope;
21433  if (!qFuzzyCompare(croppedData->at(1).x(), croppedData->at(0).x()))
21434  slope = (croppedData->at(1).y()-croppedData->at(0).y())/(croppedData->at(1).x()-croppedData->at(0).x());
21435  else
21436  slope = 0;
21437  (*croppedData)[0].setY(croppedData->at(0).y()+slope*(staticData->first().x()-croppedData->at(0).x()));
21438  (*croppedData)[0].setX(staticData->first().x());
21439 
21440  // crop upper bound:
21441  if (staticData->last().x() > croppedData->last().x()) // other one must be cropped
21442  qSwap(staticData, croppedData);
21443  int highBound = findIndexAboveX(croppedData, staticData->last().x());
21444  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
21445  croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
21446  // set highest point of cropped data to fit exactly key position of last static data point via linear interpolation:
21447  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
21448  const int li = croppedData->size()-1; // last index
21449  if (!qFuzzyCompare(croppedData->at(li).x(), croppedData->at(li-1).x()))
21450  slope = (croppedData->at(li).y()-croppedData->at(li-1).y())/(croppedData->at(li).x()-croppedData->at(li-1).x());
21451  else
21452  slope = 0;
21453  (*croppedData)[li].setY(croppedData->at(li-1).y()+slope*(staticData->last().x()-croppedData->at(li-1).x()));
21454  (*croppedData)[li].setX(staticData->last().x());
21455  } else // mKeyAxis->orientation() == Qt::Vertical
21456  {
21457  // y is key
21458  // crop lower bound:
21459  if (staticData->first().y() < croppedData->first().y()) // other one must be cropped
21460  qSwap(staticData, croppedData);
21461  int lowBound = findIndexBelowY(croppedData, staticData->first().y());
21462  if (lowBound == -1) return QPolygonF(); // key ranges have no overlap
21463  croppedData->remove(0, lowBound);
21464  // set lowest point of cropped data to fit exactly key position of first static data point via linear interpolation:
21465  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
21466  double slope;
21467  if (!qFuzzyCompare(croppedData->at(1).y(), croppedData->at(0).y())) // avoid division by zero in step plots
21468  slope = (croppedData->at(1).x()-croppedData->at(0).x())/(croppedData->at(1).y()-croppedData->at(0).y());
21469  else
21470  slope = 0;
21471  (*croppedData)[0].setX(croppedData->at(0).x()+slope*(staticData->first().y()-croppedData->at(0).y()));
21472  (*croppedData)[0].setY(staticData->first().y());
21473 
21474  // crop upper bound:
21475  if (staticData->last().y() > croppedData->last().y()) // other one must be cropped
21476  qSwap(staticData, croppedData);
21477  int highBound = findIndexAboveY(croppedData, staticData->last().y());
21478  if (highBound == -1) return QPolygonF(); // key ranges have no overlap
21479  croppedData->remove(highBound+1, croppedData->size()-(highBound+1));
21480  // set highest point of cropped data to fit exactly key position of last static data point via linear interpolation:
21481  if (croppedData->size() < 2) return QPolygonF(); // need at least two points for interpolation
21482  int li = croppedData->size()-1; // last index
21483  if (!qFuzzyCompare(croppedData->at(li).y(), croppedData->at(li-1).y())) // avoid division by zero in step plots
21484  slope = (croppedData->at(li).x()-croppedData->at(li-1).x())/(croppedData->at(li).y()-croppedData->at(li-1).y());
21485  else
21486  slope = 0;
21487  (*croppedData)[li].setX(croppedData->at(li-1).x()+slope*(staticData->last().y()-croppedData->at(li-1).y()));
21488  (*croppedData)[li].setY(staticData->last().y());
21489  }
21490 
21491  // return joined:
21492  for (int i=otherSegmentData.size()-1; i>=0; --i) // insert reversed, otherwise the polygon will be twisted
21493  thisSegmentData << otherSegmentData.at(i);
21494  return QPolygonF(thisSegmentData);
21495 }
21496 
21505 int QCPGraph::findIndexAboveX(const QVector<QPointF> *data, double x) const
21506 {
21507  for (int i=data->size()-1; i>=0; --i)
21508  {
21509  if (data->at(i).x() < x)
21510  {
21511  if (i<data->size()-1)
21512  return i+1;
21513  else
21514  return data->size()-1;
21515  }
21516  }
21517  return -1;
21518 }
21519 
21528 int QCPGraph::findIndexBelowX(const QVector<QPointF> *data, double x) const
21529 {
21530  for (int i=0; i<data->size(); ++i)
21531  {
21532  if (data->at(i).x() > x)
21533  {
21534  if (i>0)
21535  return i-1;
21536  else
21537  return 0;
21538  }
21539  }
21540  return -1;
21541 }
21542 
21551 int QCPGraph::findIndexAboveY(const QVector<QPointF> *data, double y) const
21552 {
21553  for (int i=data->size()-1; i>=0; --i)
21554  {
21555  if (data->at(i).y() < y)
21556  {
21557  if (i<data->size()-1)
21558  return i+1;
21559  else
21560  return data->size()-1;
21561  }
21562  }
21563  return -1;
21564 }
21565 
21577 double QCPGraph::pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const
21578 {
21579  closestData = mDataContainer->constEnd();
21580  if (mDataContainer->isEmpty())
21581  return -1.0;
21582  if (mLineStyle == lsNone && mScatterStyle.isNone())
21583  return -1.0;
21584 
21585  // calculate minimum distances to graph data points and find closestData iterator:
21586  double minDistSqr = std::numeric_limits<double>::max();
21587  // determine which key range comes into question, taking selection tolerance around pos into account:
21588  double posKeyMin, posKeyMax, dummy;
21589  pixelsToCoords(pixelPoint-QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMin, dummy);
21590  pixelsToCoords(pixelPoint+QPointF(mParentPlot->selectionTolerance(), mParentPlot->selectionTolerance()), posKeyMax, dummy);
21591  if (posKeyMin > posKeyMax)
21592  qSwap(posKeyMin, posKeyMax);
21593  // iterate over found data points and then choose the one with the shortest distance to pos:
21594  QCPGraphDataContainer::const_iterator begin = mDataContainer->findBegin(posKeyMin, true);
21595  QCPGraphDataContainer::const_iterator end = mDataContainer->findEnd(posKeyMax, true);
21596  for (QCPGraphDataContainer::const_iterator it=begin; it!=end; ++it)
21597  {
21598  const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared();
21599  if (currentDistSqr < minDistSqr)
21600  {
21601  minDistSqr = currentDistSqr;
21602  closestData = it;
21603  }
21604  }
21605 
21606  // calculate distance to graph line if there is one (if so, will probably be smaller than distance to closest data point):
21607  if (mLineStyle != lsNone)
21608  {
21609  // line displayed, calculate distance to line segments:
21610  QVector<QPointF> lineData;
21611  getLines(&lineData, QCPDataRange(0, dataCount()));
21612  QCPVector2D p(pixelPoint);
21613  const int step = mLineStyle==lsImpulse ? 2 : 1; // impulse plot differs from other line styles in that the lineData points are only pairwise connected
21614  for (int i=0; i<lineData.size()-1; i+=step)
21615  {
21616  const double currentDistSqr = p.distanceSquaredToLine(lineData.at(i), lineData.at(i+1));
21617  if (currentDistSqr < minDistSqr)
21618  minDistSqr = currentDistSqr;
21619  }
21620  }
21621 
21622  return qSqrt(minDistSqr);
21623 }
21624 
21633 int QCPGraph::findIndexBelowY(const QVector<QPointF> *data, double y) const
21634 {
21635  for (int i=0; i<data->size(); ++i)
21636  {
21637  if (data->at(i).y() > y)
21638  {
21639  if (i>0)
21640  return i-1;
21641  else
21642  return 0;
21643  }
21644  }
21645  return -1;
21646 }
21647 /* end of 'src/plottables/plottable-graph.cpp' */
21648 
21649 
21650 /* including file 'src/plottables/plottable-curve.cpp', size 63527 */
21651 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
21652 
21656 
21672 /* start documentation of inline functions */
21673 
21724 /* end documentation of inline functions */
21725 
21730  t(0),
21731  key(0),
21732  value(0)
21733 {
21734 }
21735 
21739 QCPCurveData::QCPCurveData(double t, double key, double value) :
21740  t(t),
21741  key(key),
21742  value(value)
21743 {
21744 }
21745 
21746 
21750 
21787 /* start of documentation of inline functions */
21788 
21796 /* end of documentation of inline functions */
21797 
21808 QCPCurve::QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis) :
21809  QCPAbstractPlottable1D<QCPCurveData>(keyAxis, valueAxis)
21810 {
21811  // modify inherited properties from abstract plottable:
21812  setPen(QPen(Qt::blue, 0));
21813  setBrush(Qt::NoBrush);
21814 
21817  setScatterSkip(0);
21818 }
21819 
21821 {
21822 }
21823 
21839 void QCPCurve::setData(QSharedPointer<QCPCurveDataContainer> data)
21840 {
21841  mDataContainer = data;
21842 }
21843 
21855 void QCPCurve::setData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
21856 {
21857  mDataContainer->clear();
21858  addData(t, keys, values, alreadySorted);
21859 }
21860 
21861 
21873 void QCPCurve::setData(const QVector<double> &keys, const QVector<double> &values)
21874 {
21875  mDataContainer->clear();
21876  addData(keys, values);
21877 }
21878 
21887 {
21888  mScatterStyle = style;
21889 }
21890 
21903 {
21904  mScatterSkip = qMax(0, skip);
21905 }
21906 
21915 {
21916  mLineStyle = style;
21917 }
21918 
21931 void QCPCurve::addData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
21932 {
21933  if (t.size() != keys.size() || t.size() != values.size())
21934  qDebug() << Q_FUNC_INFO << "ts, keys and values have different sizes:" << t.size() << keys.size() << values.size();
21935  const int n = qMin(qMin(t.size(), keys.size()), values.size());
21936  QVector<QCPCurveData> tempData(n);
21937  QVector<QCPCurveData>::iterator it = tempData.begin();
21938  const QVector<QCPCurveData>::iterator itEnd = tempData.end();
21939  int i = 0;
21940  while (it != itEnd)
21941  {
21942  it->t = t[i];
21943  it->key = keys[i];
21944  it->value = values[i];
21945  ++it;
21946  ++i;
21947  }
21948  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
21949 }
21950 
21963 void QCPCurve::addData(const QVector<double> &keys, const QVector<double> &values)
21964 {
21965  if (keys.size() != values.size())
21966  qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
21967  const int n = qMin(keys.size(), values.size());
21968  double tStart;
21969  if (!mDataContainer->isEmpty())
21970  tStart = (mDataContainer->constEnd()-1)->t + 1.0;
21971  else
21972  tStart = 0;
21973  QVector<QCPCurveData> tempData(n);
21974  QVector<QCPCurveData>::iterator it = tempData.begin();
21975  const QVector<QCPCurveData>::iterator itEnd = tempData.end();
21976  int i = 0;
21977  while (it != itEnd)
21978  {
21979  it->t = tStart + i;
21980  it->key = keys[i];
21981  it->value = values[i];
21982  ++it;
21983  ++i;
21984  }
21985  mDataContainer->add(tempData, true); // don't modify tempData beyond this to prevent copy on write
21986 }
21987 
21994 void QCPCurve::addData(double t, double key, double value)
21995 {
21996  mDataContainer->add(QCPCurveData(t, key, value));
21997 }
21998 
22009 void QCPCurve::addData(double key, double value)
22010 {
22011  if (!mDataContainer->isEmpty())
22012  mDataContainer->add(QCPCurveData((mDataContainer->constEnd()-1)->t + 1.0, key, value));
22013  else
22014  mDataContainer->add(QCPCurveData(0.0, key, value));
22015 }
22016 
22017 /* inherits documentation from base class */
22018 double QCPCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
22019 {
22020  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
22021  return -1;
22022  if (!mKeyAxis || !mValueAxis)
22023  return -1;
22024 
22025  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
22026  {
22027  QCPCurveDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
22028  double result = pointDistance(pos, closestDataPoint);
22029  if (details)
22030  {
22031  int pointIndex = closestDataPoint-mDataContainer->constBegin();
22032  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
22033  }
22034  return result;
22035  } else
22036  return -1;
22037 }
22038 
22039 /* inherits documentation from base class */
22040 QCPRange QCPCurve::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
22041 {
22042  return mDataContainer->keyRange(foundRange, inSignDomain);
22043 }
22044 
22045 /* inherits documentation from base class */
22046 QCPRange QCPCurve::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
22047 {
22048  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
22049 }
22050 
22051 /* inherits documentation from base class */
22053 {
22054  if (mDataContainer->isEmpty()) return;
22055 
22056  // allocate line vector:
22057  QVector<QPointF> lines, scatters;
22058 
22059  // loop over and draw segments of unselected/selected data:
22060  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
22061  getDataSegments(selectedSegments, unselectedSegments);
22062  allSegments << unselectedSegments << selectedSegments;
22063  for (int i=0; i<allSegments.size(); ++i)
22064  {
22065  bool isSelectedSegment = i >= unselectedSegments.size();
22066 
22067  // fill with curve data:
22068  QPen finalCurvePen = mPen; // determine the final pen already here, because the line optimization depends on its stroke width
22069  if (isSelectedSegment && mSelectionDecorator)
22070  finalCurvePen = mSelectionDecorator->pen();
22071 
22072  QCPDataRange lineDataRange = isSelectedSegment ? allSegments.at(i) : allSegments.at(i).adjusted(-1, 1); // unselected segments extend lines to bordering selected data point (safe to exceed total data bounds in first/last segment, getCurveLines takes care)
22073  getCurveLines(&lines, lineDataRange, finalCurvePen.widthF());
22074 
22075  // check data validity if flag set:
22076  #ifdef QCUSTOMPLOT_CHECK_DATA
22077  for (QCPCurveDataContainer::const_iterator it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
22078  {
22079  if (QCP::isInvalidData(it->t) ||
22080  QCP::isInvalidData(it->key, it->value))
22081  qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "invalid." << "Plottable name:" << name();
22082  }
22083  #endif
22084 
22085  // draw curve fill:
22086  applyFillAntialiasingHint(painter);
22087  if (isSelectedSegment && mSelectionDecorator)
22088  mSelectionDecorator->applyBrush(painter);
22089  else
22090  painter->setBrush(mBrush);
22091  painter->setPen(Qt::NoPen);
22092  if (painter->brush().style() != Qt::NoBrush && painter->brush().color().alpha() != 0)
22093  painter->drawPolygon(QPolygonF(lines));
22094 
22095  // draw curve line:
22096  if (mLineStyle != lsNone)
22097  {
22098  painter->setPen(finalCurvePen);
22099  painter->setBrush(Qt::NoBrush);
22100  drawCurveLine(painter, lines);
22101  }
22102 
22103  // draw scatters:
22104  QCPScatterStyle finalScatterStyle = mScatterStyle;
22105  if (isSelectedSegment && mSelectionDecorator)
22107  if (!finalScatterStyle.isNone())
22108  {
22109  getScatters(&scatters, allSegments.at(i), finalScatterStyle.size());
22110  drawScatterPlot(painter, scatters, finalScatterStyle);
22111  }
22112  }
22113 
22114  // draw other selection decoration that isn't just line/scatter pens and brushes:
22115  if (mSelectionDecorator)
22117 }
22118 
22119 /* inherits documentation from base class */
22120 void QCPCurve::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
22121 {
22122  // draw fill:
22123  if (mBrush.style() != Qt::NoBrush)
22124  {
22125  applyFillAntialiasingHint(painter);
22126  painter->fillRect(QRectF(rect.left(), rect.top()+rect.height()/2.0, rect.width(), rect.height()/3.0), mBrush);
22127  }
22128  // draw line vertically centered:
22129  if (mLineStyle != lsNone)
22130  {
22132  painter->setPen(mPen);
22133  painter->drawLine(QLineF(rect.left(), rect.top()+rect.height()/2.0, rect.right()+5, rect.top()+rect.height()/2.0)); // +5 on x2 else last segment is missing from dashed/dotted pens
22134  }
22135  // draw scatter symbol:
22136  if (!mScatterStyle.isNone())
22137  {
22139  // scale scatter pixmap if it's too large to fit in legend icon rect:
22140  if (mScatterStyle.shape() == QCPScatterStyle::ssPixmap && (mScatterStyle.pixmap().size().width() > rect.width() || mScatterStyle.pixmap().size().height() > rect.height()))
22141  {
22142  QCPScatterStyle scaledStyle(mScatterStyle);
22143  scaledStyle.setPixmap(scaledStyle.pixmap().scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
22144  scaledStyle.applyTo(painter, mPen);
22145  scaledStyle.drawShape(painter, QRectF(rect).center());
22146  } else
22147  {
22148  mScatterStyle.applyTo(painter, mPen);
22149  mScatterStyle.drawShape(painter, QRectF(rect).center());
22150  }
22151  }
22152 }
22153 
22160 void QCPCurve::drawCurveLine(QCPPainter *painter, const QVector<QPointF> &lines) const
22161 {
22162  if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
22163  {
22165  drawPolyline(painter, lines);
22166  }
22167 }
22168 
22176 void QCPCurve::drawScatterPlot(QCPPainter *painter, const QVector<QPointF> &points, const QCPScatterStyle &style) const
22177 {
22178  // draw scatter point symbols:
22180  style.applyTo(painter, mPen);
22181  for (int i=0; i<points.size(); ++i)
22182  if (!qIsNaN(points.at(i).x()) && !qIsNaN(points.at(i).y()))
22183  style.drawShape(painter, points.at(i));
22184 }
22185 
22214 void QCPCurve::getCurveLines(QVector<QPointF> *lines, const QCPDataRange &dataRange, double penWidth) const
22215 {
22216  if (!lines) return;
22217  lines->clear();
22218  QCPAxis *keyAxis = mKeyAxis.data();
22219  QCPAxis *valueAxis = mValueAxis.data();
22220  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
22221 
22222  // add margins to rect to compensate for stroke width
22223  const double strokeMargin = qMax(qreal(1.0), qreal(penWidth*0.75)); // stroke radius + 50% safety
22224  const double keyMin = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().lower)-strokeMargin*keyAxis->pixelOrientation());
22225  const double keyMax = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyAxis->range().upper)+strokeMargin*keyAxis->pixelOrientation());
22226  const double valueMin = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().lower)-strokeMargin*valueAxis->pixelOrientation());
22227  const double valueMax = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueAxis->range().upper)+strokeMargin*valueAxis->pixelOrientation());
22228  QCPCurveDataContainer::const_iterator itBegin = mDataContainer->constBegin();
22230  mDataContainer->limitIteratorsToDataRange(itBegin, itEnd, dataRange);
22231  if (itBegin == itEnd)
22232  return;
22234  QCPCurveDataContainer::const_iterator prevIt = itEnd-1;
22235  int prevRegion = getRegion(prevIt->key, prevIt->value, keyMin, valueMax, keyMax, valueMin);
22236  QVector<QPointF> trailingPoints; // points that must be applied after all other points (are generated only when handling first point to get virtual segment between last and first point right)
22237  while (it != itEnd)
22238  {
22239  const int currentRegion = getRegion(it->key, it->value, keyMin, valueMax, keyMax, valueMin);
22240  if (currentRegion != prevRegion) // changed region, possibly need to add some optimized edge points or original points if entering R
22241  {
22242  if (currentRegion != 5) // segment doesn't end in R, so it's a candidate for removal
22243  {
22244  QPointF crossA, crossB;
22245  if (prevRegion == 5) // we're coming from R, so add this point optimized
22246  {
22247  lines->append(getOptimizedPoint(currentRegion, it->key, it->value, prevIt->key, prevIt->value, keyMin, valueMax, keyMax, valueMin));
22248  // in the situations 5->1/7/9/3 the segment may leave R and directly cross through two outer regions. In these cases we need to add an additional corner point
22249  *lines << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);
22250  } else if (mayTraverse(prevRegion, currentRegion) &&
22251  getTraverse(prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin, crossA, crossB))
22252  {
22253  // add the two cross points optimized if segment crosses R and if segment isn't virtual zeroth segment between last and first curve point:
22254  QVector<QPointF> beforeTraverseCornerPoints, afterTraverseCornerPoints;
22255  getTraverseCornerPoints(prevRegion, currentRegion, keyMin, valueMax, keyMax, valueMin, beforeTraverseCornerPoints, afterTraverseCornerPoints);
22256  if (it != itBegin)
22257  {
22258  *lines << beforeTraverseCornerPoints;
22259  lines->append(crossA);
22260  lines->append(crossB);
22261  *lines << afterTraverseCornerPoints;
22262  } else
22263  {
22264  lines->append(crossB);
22265  *lines << afterTraverseCornerPoints;
22266  trailingPoints << beforeTraverseCornerPoints << crossA ;
22267  }
22268  } else // doesn't cross R, line is just moving around in outside regions, so only need to add optimized point(s) at the boundary corner(s)
22269  {
22270  *lines << getOptimizedCornerPoints(prevRegion, currentRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);
22271  }
22272  } else // segment does end in R, so we add previous point optimized and this point at original position
22273  {
22274  if (it == itBegin) // it is first point in curve and prevIt is last one. So save optimized point for adding it to the lineData in the end
22275  trailingPoints << getOptimizedPoint(prevRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin);
22276  else
22277  lines->append(getOptimizedPoint(prevRegion, prevIt->key, prevIt->value, it->key, it->value, keyMin, valueMax, keyMax, valueMin));
22278  lines->append(coordsToPixels(it->key, it->value));
22279  }
22280  } else // region didn't change
22281  {
22282  if (currentRegion == 5) // still in R, keep adding original points
22283  {
22284  lines->append(coordsToPixels(it->key, it->value));
22285  } else // still outside R, no need to add anything
22286  {
22287  // see how this is not doing anything? That's the main optimization...
22288  }
22289  }
22290  prevIt = it;
22291  prevRegion = currentRegion;
22292  ++it;
22293  }
22294  *lines << trailingPoints;
22295 }
22296 
22317 void QCPCurve::getScatters(QVector<QPointF> *scatters, const QCPDataRange &dataRange, double scatterWidth) const
22318 {
22319  if (!scatters) return;
22320  scatters->clear();
22321  QCPAxis *keyAxis = mKeyAxis.data();
22322  QCPAxis *valueAxis = mValueAxis.data();
22323  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
22324 
22327  mDataContainer->limitIteratorsToDataRange(begin, end, dataRange);
22328  if (begin == end)
22329  return;
22330  const int scatterModulo = mScatterSkip+1;
22331  const bool doScatterSkip = mScatterSkip > 0;
22332  int endIndex = end-mDataContainer->constBegin();
22333 
22334  QCPRange keyRange = keyAxis->range();
22335  QCPRange valueRange = valueAxis->range();
22336  // extend range to include width of scatter symbols:
22337  keyRange.lower = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyRange.lower)-scatterWidth*keyAxis->pixelOrientation());
22338  keyRange.upper = keyAxis->pixelToCoord(keyAxis->coordToPixel(keyRange.upper)+scatterWidth*keyAxis->pixelOrientation());
22339  valueRange.lower = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueRange.lower)-scatterWidth*valueAxis->pixelOrientation());
22340  valueRange.upper = valueAxis->pixelToCoord(valueAxis->coordToPixel(valueRange.upper)+scatterWidth*valueAxis->pixelOrientation());
22341 
22343  int itIndex = begin-mDataContainer->constBegin();
22344  while (doScatterSkip && it != end && itIndex % scatterModulo != 0) // advance begin iterator to first non-skipped scatter
22345  {
22346  ++itIndex;
22347  ++it;
22348  }
22349  if (keyAxis->orientation() == Qt::Vertical)
22350  {
22351  while (it != end)
22352  {
22353  if (!qIsNaN(it->value) && keyRange.contains(it->key) && valueRange.contains(it->value))
22354  scatters->append(QPointF(valueAxis->coordToPixel(it->value), keyAxis->coordToPixel(it->key)));
22355 
22356  // advance iterator to next (non-skipped) data point:
22357  if (!doScatterSkip)
22358  ++it;
22359  else
22360  {
22361  itIndex += scatterModulo;
22362  if (itIndex < endIndex) // make sure we didn't jump over end
22363  it += scatterModulo;
22364  else
22365  {
22366  it = end;
22367  itIndex = endIndex;
22368  }
22369  }
22370  }
22371  } else
22372  {
22373  while (it != end)
22374  {
22375  if (!qIsNaN(it->value) && keyRange.contains(it->key) && valueRange.contains(it->value))
22376  scatters->append(QPointF(keyAxis->coordToPixel(it->key), valueAxis->coordToPixel(it->value)));
22377 
22378  // advance iterator to next (non-skipped) data point:
22379  if (!doScatterSkip)
22380  ++it;
22381  else
22382  {
22383  itIndex += scatterModulo;
22384  if (itIndex < endIndex) // make sure we didn't jump over end
22385  it += scatterModulo;
22386  else
22387  {
22388  it = end;
22389  itIndex = endIndex;
22390  }
22391  }
22392  }
22393  }
22394 }
22395 
22415 int QCPCurve::getRegion(double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
22416 {
22417  if (key < keyMin) // region 123
22418  {
22419  if (value > valueMax)
22420  return 1;
22421  else if (value < valueMin)
22422  return 3;
22423  else
22424  return 2;
22425  } else if (key > keyMax) // region 789
22426  {
22427  if (value > valueMax)
22428  return 7;
22429  else if (value < valueMin)
22430  return 9;
22431  else
22432  return 8;
22433  } else // region 456
22434  {
22435  if (value > valueMax)
22436  return 4;
22437  else if (value < valueMin)
22438  return 6;
22439  else
22440  return 5;
22441  }
22442 }
22443 
22459 QPointF QCPCurve::getOptimizedPoint(int otherRegion, double otherKey, double otherValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
22460 {
22461  // The intersection point interpolation here is done in pixel coordinates, so we don't need to
22462  // differentiate between different axis scale types. Note that the nomenclature
22463  // top/left/bottom/right/min/max is with respect to the rect in plot coordinates, wich may be
22464  // different in pixel coordinates (horz/vert key axes, reversed ranges)
22465 
22466  const double keyMinPx = mKeyAxis->coordToPixel(keyMin);
22467  const double keyMaxPx = mKeyAxis->coordToPixel(keyMax);
22468  const double valueMinPx = mValueAxis->coordToPixel(valueMin);
22469  const double valueMaxPx = mValueAxis->coordToPixel(valueMax);
22470  const double otherValuePx = mValueAxis->coordToPixel(otherValue);
22471  const double valuePx = mValueAxis->coordToPixel(value);
22472  const double otherKeyPx = mKeyAxis->coordToPixel(otherKey);
22473  const double keyPx = mKeyAxis->coordToPixel(key);
22474  double intersectKeyPx = keyMinPx; // initial key just a fail-safe
22475  double intersectValuePx = valueMinPx; // initial value just a fail-safe
22476  switch (otherRegion)
22477  {
22478  case 1: // top and left edge
22479  {
22480  intersectValuePx = valueMaxPx;
22481  intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
22482  if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether top edge is not intersected, then it must be left edge (qMin/qMax necessary since axes may be reversed)
22483  {
22484  intersectKeyPx = keyMinPx;
22485  intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
22486  }
22487  break;
22488  }
22489  case 2: // left edge
22490  {
22491  intersectKeyPx = keyMinPx;
22492  intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
22493  break;
22494  }
22495  case 3: // bottom and left edge
22496  {
22497  intersectValuePx = valueMinPx;
22498  intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
22499  if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether bottom edge is not intersected, then it must be left edge (qMin/qMax necessary since axes may be reversed)
22500  {
22501  intersectKeyPx = keyMinPx;
22502  intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
22503  }
22504  break;
22505  }
22506  case 4: // top edge
22507  {
22508  intersectValuePx = valueMaxPx;
22509  intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
22510  break;
22511  }
22512  case 5:
22513  {
22514  break; // case 5 shouldn't happen for this function but we add it anyway to prevent potential discontinuity in branch table
22515  }
22516  case 6: // bottom edge
22517  {
22518  intersectValuePx = valueMinPx;
22519  intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
22520  break;
22521  }
22522  case 7: // top and right edge
22523  {
22524  intersectValuePx = valueMaxPx;
22525  intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
22526  if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether top edge is not intersected, then it must be right edge (qMin/qMax necessary since axes may be reversed)
22527  {
22528  intersectKeyPx = keyMaxPx;
22529  intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
22530  }
22531  break;
22532  }
22533  case 8: // right edge
22534  {
22535  intersectKeyPx = keyMaxPx;
22536  intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
22537  break;
22538  }
22539  case 9: // bottom and right edge
22540  {
22541  intersectValuePx = valueMinPx;
22542  intersectKeyPx = otherKeyPx + (keyPx-otherKeyPx)/(valuePx-otherValuePx)*(intersectValuePx-otherValuePx);
22543  if (intersectKeyPx < qMin(keyMinPx, keyMaxPx) || intersectKeyPx > qMax(keyMinPx, keyMaxPx)) // check whether bottom edge is not intersected, then it must be right edge (qMin/qMax necessary since axes may be reversed)
22544  {
22545  intersectKeyPx = keyMaxPx;
22546  intersectValuePx = otherValuePx + (valuePx-otherValuePx)/(keyPx-otherKeyPx)*(intersectKeyPx-otherKeyPx);
22547  }
22548  break;
22549  }
22550  }
22551  if (mKeyAxis->orientation() == Qt::Horizontal)
22552  return QPointF(intersectKeyPx, intersectValuePx);
22553  else
22554  return QPointF(intersectValuePx, intersectKeyPx);
22555 }
22556 
22575 QVector<QPointF> QCPCurve::getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
22576 {
22577  QVector<QPointF> result;
22578  switch (prevRegion)
22579  {
22580  case 1:
22581  {
22582  switch (currentRegion)
22583  {
22584  case 2: { result << coordsToPixels(keyMin, valueMax); break; }
22585  case 4: { result << coordsToPixels(keyMin, valueMax); break; }
22586  case 3: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); break; }
22587  case 7: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); break; }
22588  case 6: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
22589  case 8: { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
22590  case 9: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
22591  if ((value-prevValue)/(key-prevKey)*(keyMin-key)+value < valueMin) // segment passes below R
22592  { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); }
22593  else
22594  { result << coordsToPixels(keyMin, valueMax) << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); }
22595  break;
22596  }
22597  }
22598  break;
22599  }
22600  case 2:
22601  {
22602  switch (currentRegion)
22603  {
22604  case 1: { result << coordsToPixels(keyMin, valueMax); break; }
22605  case 3: { result << coordsToPixels(keyMin, valueMin); break; }
22606  case 4: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
22607  case 6: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
22608  case 7: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); break; }
22609  case 9: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); break; }
22610  }
22611  break;
22612  }
22613  case 3:
22614  {
22615  switch (currentRegion)
22616  {
22617  case 2: { result << coordsToPixels(keyMin, valueMin); break; }
22618  case 6: { result << coordsToPixels(keyMin, valueMin); break; }
22619  case 1: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); break; }
22620  case 9: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); break; }
22621  case 4: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
22622  case 8: { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
22623  case 7: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
22624  if ((value-prevValue)/(key-prevKey)*(keyMax-key)+value < valueMin) // segment passes below R
22625  { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); }
22626  else
22627  { result << coordsToPixels(keyMin, valueMin) << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); }
22628  break;
22629  }
22630  }
22631  break;
22632  }
22633  case 4:
22634  {
22635  switch (currentRegion)
22636  {
22637  case 1: { result << coordsToPixels(keyMin, valueMax); break; }
22638  case 7: { result << coordsToPixels(keyMax, valueMax); break; }
22639  case 2: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
22640  case 8: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
22641  case 3: { result << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); break; }
22642  case 9: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMax, valueMin); break; }
22643  }
22644  break;
22645  }
22646  case 5:
22647  {
22648  switch (currentRegion)
22649  {
22650  case 1: { result << coordsToPixels(keyMin, valueMax); break; }
22651  case 7: { result << coordsToPixels(keyMax, valueMax); break; }
22652  case 9: { result << coordsToPixels(keyMax, valueMin); break; }
22653  case 3: { result << coordsToPixels(keyMin, valueMin); break; }
22654  }
22655  break;
22656  }
22657  case 6:
22658  {
22659  switch (currentRegion)
22660  {
22661  case 3: { result << coordsToPixels(keyMin, valueMin); break; }
22662  case 9: { result << coordsToPixels(keyMax, valueMin); break; }
22663  case 2: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
22664  case 8: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
22665  case 1: { result << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); break; }
22666  case 7: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMax, valueMax); break; }
22667  }
22668  break;
22669  }
22670  case 7:
22671  {
22672  switch (currentRegion)
22673  {
22674  case 4: { result << coordsToPixels(keyMax, valueMax); break; }
22675  case 8: { result << coordsToPixels(keyMax, valueMax); break; }
22676  case 1: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); break; }
22677  case 9: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); break; }
22678  case 2: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); result.append(result.last()); break; }
22679  case 6: { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
22680  case 3: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
22681  if ((value-prevValue)/(key-prevKey)*(keyMax-key)+value < valueMin) // segment passes below R
22682  { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); }
22683  else
22684  { result << coordsToPixels(keyMax, valueMax) << coordsToPixels(keyMin, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); }
22685  break;
22686  }
22687  }
22688  break;
22689  }
22690  case 8:
22691  {
22692  switch (currentRegion)
22693  {
22694  case 7: { result << coordsToPixels(keyMax, valueMax); break; }
22695  case 9: { result << coordsToPixels(keyMax, valueMin); break; }
22696  case 4: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
22697  case 6: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); break; }
22698  case 1: { result << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); break; }
22699  case 3: { result << coordsToPixels(keyMax, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMin); break; }
22700  }
22701  break;
22702  }
22703  case 9:
22704  {
22705  switch (currentRegion)
22706  {
22707  case 6: { result << coordsToPixels(keyMax, valueMin); break; }
22708  case 8: { result << coordsToPixels(keyMax, valueMin); break; }
22709  case 3: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); break; }
22710  case 7: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); break; }
22711  case 2: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); result.append(result.last()); break; }
22712  case 4: { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); result.append(result.last()); break; }
22713  case 1: { // in this case we need another distinction of cases: segment may pass below or above rect, requiring either bottom right or top left corner points
22714  if ((value-prevValue)/(key-prevKey)*(keyMin-key)+value < valueMin) // segment passes below R
22715  { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMin, valueMin); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); }
22716  else
22717  { result << coordsToPixels(keyMax, valueMin) << coordsToPixels(keyMax, valueMax); result.append(result.last()); result << coordsToPixels(keyMin, valueMax); }
22718  break;
22719  }
22720  }
22721  break;
22722  }
22723  }
22724  return result;
22725 }
22726 
22739 bool QCPCurve::mayTraverse(int prevRegion, int currentRegion) const
22740 {
22741  switch (prevRegion)
22742  {
22743  case 1:
22744  {
22745  switch (currentRegion)
22746  {
22747  case 4:
22748  case 7:
22749  case 2:
22750  case 3: return false;
22751  default: return true;
22752  }
22753  }
22754  case 2:
22755  {
22756  switch (currentRegion)
22757  {
22758  case 1:
22759  case 3: return false;
22760  default: return true;
22761  }
22762  }
22763  case 3:
22764  {
22765  switch (currentRegion)
22766  {
22767  case 1:
22768  case 2:
22769  case 6:
22770  case 9: return false;
22771  default: return true;
22772  }
22773  }
22774  case 4:
22775  {
22776  switch (currentRegion)
22777  {
22778  case 1:
22779  case 7: return false;
22780  default: return true;
22781  }
22782  }
22783  case 5: return false; // should never occur
22784  case 6:
22785  {
22786  switch (currentRegion)
22787  {
22788  case 3:
22789  case 9: return false;
22790  default: return true;
22791  }
22792  }
22793  case 7:
22794  {
22795  switch (currentRegion)
22796  {
22797  case 1:
22798  case 4:
22799  case 8:
22800  case 9: return false;
22801  default: return true;
22802  }
22803  }
22804  case 8:
22805  {
22806  switch (currentRegion)
22807  {
22808  case 7:
22809  case 9: return false;
22810  default: return true;
22811  }
22812  }
22813  case 9:
22814  {
22815  switch (currentRegion)
22816  {
22817  case 3:
22818  case 6:
22819  case 8:
22820  case 7: return false;
22821  default: return true;
22822  }
22823  }
22824  default: return true;
22825  }
22826 }
22827 
22828 
22842 bool QCPCurve::getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const
22843 {
22844  // The intersection point interpolation here is done in pixel coordinates, so we don't need to
22845  // differentiate between different axis scale types. Note that the nomenclature
22846  // top/left/bottom/right/min/max is with respect to the rect in plot coordinates, wich may be
22847  // different in pixel coordinates (horz/vert key axes, reversed ranges)
22848 
22849  QList<QPointF> intersections;
22850  const double valueMinPx = mValueAxis->coordToPixel(valueMin);
22851  const double valueMaxPx = mValueAxis->coordToPixel(valueMax);
22852  const double keyMinPx = mKeyAxis->coordToPixel(keyMin);
22853  const double keyMaxPx = mKeyAxis->coordToPixel(keyMax);
22854  const double keyPx = mKeyAxis->coordToPixel(key);
22855  const double valuePx = mValueAxis->coordToPixel(value);
22856  const double prevKeyPx = mKeyAxis->coordToPixel(prevKey);
22857  const double prevValuePx = mValueAxis->coordToPixel(prevValue);
22858  if (qFuzzyIsNull(key-prevKey)) // line is parallel to value axis
22859  {
22860  // due to region filter in mayTraverse(), if line is parallel to value or key axis, region 5 is traversed here
22861  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyPx, valueMinPx) : QPointF(valueMinPx, keyPx)); // direction will be taken care of at end of method
22862  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyPx, valueMaxPx) : QPointF(valueMaxPx, keyPx));
22863  } else if (qFuzzyIsNull(value-prevValue)) // line is parallel to key axis
22864  {
22865  // due to region filter in mayTraverse(), if line is parallel to value or key axis, region 5 is traversed here
22866  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMinPx, valuePx) : QPointF(valuePx, keyMinPx)); // direction will be taken care of at end of method
22867  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMaxPx, valuePx) : QPointF(valuePx, keyMaxPx));
22868  } else // line is skewed
22869  {
22870  double gamma;
22871  double keyPerValuePx = (keyPx-prevKeyPx)/(valuePx-prevValuePx);
22872  // check top of rect:
22873  gamma = prevKeyPx + (valueMaxPx-prevValuePx)*keyPerValuePx;
22874  if (gamma >= qMin(keyMinPx, keyMaxPx) && gamma <= qMax(keyMinPx, keyMaxPx)) // qMin/qMax necessary since axes may be reversed
22875  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(gamma, valueMaxPx) : QPointF(valueMaxPx, gamma));
22876  // check bottom of rect:
22877  gamma = prevKeyPx + (valueMinPx-prevValuePx)*keyPerValuePx;
22878  if (gamma >= qMin(keyMinPx, keyMaxPx) && gamma <= qMax(keyMinPx, keyMaxPx)) // qMin/qMax necessary since axes may be reversed
22879  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(gamma, valueMinPx) : QPointF(valueMinPx, gamma));
22880  const double valuePerKeyPx = 1.0/keyPerValuePx;
22881  // check left of rect:
22882  gamma = prevValuePx + (keyMinPx-prevKeyPx)*valuePerKeyPx;
22883  if (gamma >= qMin(valueMinPx, valueMaxPx) && gamma <= qMax(valueMinPx, valueMaxPx)) // qMin/qMax necessary since axes may be reversed
22884  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMinPx, gamma) : QPointF(gamma, keyMinPx));
22885  // check right of rect:
22886  gamma = prevValuePx + (keyMaxPx-prevKeyPx)*valuePerKeyPx;
22887  if (gamma >= qMin(valueMinPx, valueMaxPx) && gamma <= qMax(valueMinPx, valueMaxPx)) // qMin/qMax necessary since axes may be reversed
22888  intersections.append(mKeyAxis->orientation() == Qt::Horizontal ? QPointF(keyMaxPx, gamma) : QPointF(gamma, keyMaxPx));
22889  }
22890 
22891  // handle cases where found points isn't exactly 2:
22892  if (intersections.size() > 2)
22893  {
22894  // line probably goes through corner of rect, and we got duplicate points there. single out the point pair with greatest distance in between:
22895  double distSqrMax = 0;
22896  QPointF pv1, pv2;
22897  for (int i=0; i<intersections.size()-1; ++i)
22898  {
22899  for (int k=i+1; k<intersections.size(); ++k)
22900  {
22901  QPointF distPoint = intersections.at(i)-intersections.at(k);
22902  double distSqr = distPoint.x()*distPoint.x()+distPoint.y()+distPoint.y();
22903  if (distSqr > distSqrMax)
22904  {
22905  pv1 = intersections.at(i);
22906  pv2 = intersections.at(k);
22907  distSqrMax = distSqr;
22908  }
22909  }
22910  }
22911  intersections = QList<QPointF>() << pv1 << pv2;
22912  } else if (intersections.size() != 2)
22913  {
22914  // one or even zero points found (shouldn't happen unless line perfectly tangent to corner), no need to draw segment
22915  return false;
22916  }
22917 
22918  // possibly re-sort points so optimized point segment has same direction as original segment:
22919  double xDelta = keyPx-prevKeyPx;
22920  double yDelta = valuePx-prevValuePx;
22921  if (mKeyAxis->orientation() != Qt::Horizontal)
22922  qSwap(xDelta, yDelta);
22923  if (xDelta*(intersections.at(1).x()-intersections.at(0).x()) + yDelta*(intersections.at(1).y()-intersections.at(0).y()) < 0) // scalar product of both segments < 0 -> opposite direction
22924  intersections.move(0, 1);
22925  crossA = intersections.at(0);
22926  crossB = intersections.at(1);
22927  return true;
22928 }
22929 
22955 void QCPCurve::getTraverseCornerPoints(int prevRegion, int currentRegion, double keyMin, double valueMax, double keyMax, double valueMin, QVector<QPointF> &beforeTraverse, QVector<QPointF> &afterTraverse) const
22956 {
22957  switch (prevRegion)
22958  {
22959  case 1:
22960  {
22961  switch (currentRegion)
22962  {
22963  case 6: { beforeTraverse << coordsToPixels(keyMin, valueMax); break; }
22964  case 9: { beforeTraverse << coordsToPixels(keyMin, valueMax); afterTraverse << coordsToPixels(keyMax, valueMin); break; }
22965  case 8: { beforeTraverse << coordsToPixels(keyMin, valueMax); break; }
22966  }
22967  break;
22968  }
22969  case 2:
22970  {
22971  switch (currentRegion)
22972  {
22973  case 7: { afterTraverse << coordsToPixels(keyMax, valueMax); break; }
22974  case 9: { afterTraverse << coordsToPixels(keyMax, valueMin); break; }
22975  }
22976  break;
22977  }
22978  case 3:
22979  {
22980  switch (currentRegion)
22981  {
22982  case 4: { beforeTraverse << coordsToPixels(keyMin, valueMin); break; }
22983  case 7: { beforeTraverse << coordsToPixels(keyMin, valueMin); afterTraverse << coordsToPixels(keyMax, valueMax); break; }
22984  case 8: { beforeTraverse << coordsToPixels(keyMin, valueMin); break; }
22985  }
22986  break;
22987  }
22988  case 4:
22989  {
22990  switch (currentRegion)
22991  {
22992  case 3: { afterTraverse << coordsToPixels(keyMin, valueMin); break; }
22993  case 9: { afterTraverse << coordsToPixels(keyMax, valueMin); break; }
22994  }
22995  break;
22996  }
22997  case 5: { break; } // shouldn't happen because this method only handles full traverses
22998  case 6:
22999  {
23000  switch (currentRegion)
23001  {
23002  case 1: { afterTraverse << coordsToPixels(keyMin, valueMax); break; }
23003  case 7: { afterTraverse << coordsToPixels(keyMax, valueMax); break; }
23004  }
23005  break;
23006  }
23007  case 7:
23008  {
23009  switch (currentRegion)
23010  {
23011  case 2: { beforeTraverse << coordsToPixels(keyMax, valueMax); break; }
23012  case 3: { beforeTraverse << coordsToPixels(keyMax, valueMax); afterTraverse << coordsToPixels(keyMin, valueMin); break; }
23013  case 6: { beforeTraverse << coordsToPixels(keyMax, valueMax); break; }
23014  }
23015  break;
23016  }
23017  case 8:
23018  {
23019  switch (currentRegion)
23020  {
23021  case 1: { afterTraverse << coordsToPixels(keyMin, valueMax); break; }
23022  case 3: { afterTraverse << coordsToPixels(keyMin, valueMin); break; }
23023  }
23024  break;
23025  }
23026  case 9:
23027  {
23028  switch (currentRegion)
23029  {
23030  case 2: { beforeTraverse << coordsToPixels(keyMax, valueMin); break; }
23031  case 1: { beforeTraverse << coordsToPixels(keyMax, valueMin); afterTraverse << coordsToPixels(keyMin, valueMax); break; }
23032  case 4: { beforeTraverse << coordsToPixels(keyMax, valueMin); break; }
23033  }
23034  break;
23035  }
23036  }
23037 }
23038 
23051 double QCPCurve::pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer::const_iterator &closestData) const
23052 {
23053  closestData = mDataContainer->constEnd();
23054  if (mDataContainer->isEmpty())
23055  return -1.0;
23056  if (mLineStyle == lsNone && mScatterStyle.isNone())
23057  return -1.0;
23058 
23059  if (mDataContainer->size() == 1)
23060  {
23061  QPointF dataPoint = coordsToPixels(mDataContainer->constBegin()->key, mDataContainer->constBegin()->value);
23062  closestData = mDataContainer->constBegin();
23063  return QCPVector2D(dataPoint-pixelPoint).length();
23064  }
23065 
23066  // calculate minimum distances to curve data points and find closestData iterator:
23067  double minDistSqr = std::numeric_limits<double>::max();
23068  // iterate over found data points and then choose the one with the shortest distance to pos:
23071  for (QCPCurveDataContainer::const_iterator it=begin; it!=end; ++it)
23072  {
23073  const double currentDistSqr = QCPVector2D(coordsToPixels(it->key, it->value)-pixelPoint).lengthSquared();
23074  if (currentDistSqr < minDistSqr)
23075  {
23076  minDistSqr = currentDistSqr;
23077  closestData = it;
23078  }
23079  }
23080 
23081  // calculate distance to line if there is one (if so, will probably be smaller than distance to closest data point):
23082  if (mLineStyle != lsNone)
23083  {
23084  QVector<QPointF> lines;
23085  getCurveLines(&lines, QCPDataRange(0, dataCount()), mParentPlot->selectionTolerance()*1.2); // optimized lines outside axis rect shouldn't respond to clicks at the edge, so use 1.2*tolerance as pen width
23086  for (int i=0; i<lines.size()-1; ++i)
23087  {
23088  double currentDistSqr = QCPVector2D(pixelPoint).distanceSquaredToLine(lines.at(i), lines.at(i+1));
23089  if (currentDistSqr < minDistSqr)
23090  minDistSqr = currentDistSqr;
23091  }
23092  }
23093 
23094  return qSqrt(minDistSqr);
23095 }
23096 /* end of 'src/plottables/plottable-curve.cpp' */
23097 
23098 
23099 /* including file 'src/plottables/plottable-bars.cpp', size 43512 */
23100 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
23101 
23102 
23106 
23141 /* start of documentation of inline functions */
23142 
23169 /* end of documentation of inline functions */
23170 
23175  QObject(parentPlot),
23176  mParentPlot(parentPlot),
23177  mSpacingType(stAbsolute),
23178  mSpacing(4)
23179 {
23180 }
23181 
23183 {
23184  clear();
23185 }
23186 
23195 {
23197 }
23198 
23206 {
23207  mSpacing = spacing;
23208 }
23209 
23216 QCPBars *QCPBarsGroup::bars(int index) const
23217 {
23218  if (index >= 0 && index < mBars.size())
23219  {
23220  return mBars.at(index);
23221  } else
23222  {
23223  qDebug() << Q_FUNC_INFO << "index out of bounds:" << index;
23224  return 0;
23225  }
23226 }
23227 
23234 {
23235  foreach (QCPBars *bars, mBars) // since foreach takes a copy, removing bars in the loop is okay
23236  bars->setBarsGroup(0); // removes itself via removeBars
23237 }
23238 
23246 {
23247  if (!bars)
23248  {
23249  qDebug() << Q_FUNC_INFO << "bars is 0";
23250  return;
23251  }
23252 
23253  if (!mBars.contains(bars))
23254  bars->setBarsGroup(this);
23255  else
23256  qDebug() << Q_FUNC_INFO << "bars plottable is already in this bars group:" << reinterpret_cast<quintptr>(bars);
23257 }
23258 
23269 {
23270  if (!bars)
23271  {
23272  qDebug() << Q_FUNC_INFO << "bars is 0";
23273  return;
23274  }
23275 
23276  // first append to bars list normally:
23277  if (!mBars.contains(bars))
23278  bars->setBarsGroup(this);
23279  // then move to according position:
23280  mBars.move(mBars.indexOf(bars), qBound(0, i, mBars.size()-1));
23281 }
23282 
23289 {
23290  if (!bars)
23291  {
23292  qDebug() << Q_FUNC_INFO << "bars is 0";
23293  return;
23294  }
23295 
23296  if (mBars.contains(bars))
23297  bars->setBarsGroup(0);
23298  else
23299  qDebug() << Q_FUNC_INFO << "bars plottable is not in this bars group:" << reinterpret_cast<quintptr>(bars);
23300 }
23301 
23310 {
23311  if (!mBars.contains(bars))
23312  mBars.append(bars);
23313 }
23314 
23323 {
23324  mBars.removeOne(bars);
23325 }
23326 
23333 double QCPBarsGroup::keyPixelOffset(const QCPBars *bars, double keyCoord)
23334 {
23335  // find list of all base bars in case some mBars are stacked:
23336  QList<const QCPBars*> baseBars;
23337  foreach (const QCPBars *b, mBars)
23338  {
23339  while (b->barBelow())
23340  b = b->barBelow();
23341  if (!baseBars.contains(b))
23342  baseBars.append(b);
23343  }
23344  // find base bar this "bars" is stacked on:
23345  const QCPBars *thisBase = bars;
23346  while (thisBase->barBelow())
23347  thisBase = thisBase->barBelow();
23348 
23349  // determine key pixel offset of this base bars considering all other base bars in this barsgroup:
23350  double result = 0;
23351  int index = baseBars.indexOf(thisBase);
23352  if (index >= 0)
23353  {
23354  if (baseBars.size() % 2 == 1 && index == (baseBars.size()-1)/2) // is center bar (int division on purpose)
23355  {
23356  return result;
23357  } else
23358  {
23359  double lowerPixelWidth, upperPixelWidth;
23360  int startIndex;
23361  int dir = (index <= (baseBars.size()-1)/2) ? -1 : 1; // if bar is to lower keys of center, dir is negative
23362  if (baseBars.size() % 2 == 0) // even number of bars
23363  {
23364  startIndex = baseBars.size()/2 + (dir < 0 ? -1 : 0);
23365  result += getPixelSpacing(baseBars.at(startIndex), keyCoord)*0.5; // half of middle spacing
23366  } else // uneven number of bars
23367  {
23368  startIndex = (baseBars.size()-1)/2+dir;
23369  baseBars.at((baseBars.size()-1)/2)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
23370  result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5; // half of center bar
23371  result += getPixelSpacing(baseBars.at((baseBars.size()-1)/2), keyCoord); // center bar spacing
23372  }
23373  for (int i = startIndex; i != index; i += dir) // add widths and spacings of bars in between center and our bars
23374  {
23375  baseBars.at(i)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
23376  result += qAbs(upperPixelWidth-lowerPixelWidth);
23377  result += getPixelSpacing(baseBars.at(i), keyCoord);
23378  }
23379  // finally half of our bars width:
23380  baseBars.at(index)->getPixelWidth(keyCoord, lowerPixelWidth, upperPixelWidth);
23381  result += qAbs(upperPixelWidth-lowerPixelWidth)*0.5;
23382  // correct sign of result depending on orientation and direction of key axis:
23383  result *= dir*thisBase->keyAxis()->pixelOrientation();
23384  }
23385  }
23386  return result;
23387 }
23388 
23399 double QCPBarsGroup::getPixelSpacing(const QCPBars *bars, double keyCoord)
23400 {
23401  switch (mSpacingType)
23402  {
23403  case stAbsolute:
23404  {
23405  return mSpacing;
23406  }
23407  case stAxisRectRatio:
23408  {
23409  if (bars->keyAxis()->orientation() == Qt::Horizontal)
23410  return bars->keyAxis()->axisRect()->width()*mSpacing;
23411  else
23412  return bars->keyAxis()->axisRect()->height()*mSpacing;
23413  }
23414  case stPlotCoords:
23415  {
23416  double keyPixel = bars->keyAxis()->coordToPixel(keyCoord);
23417  return qAbs(bars->keyAxis()->coordToPixel(keyCoord+mSpacing)-keyPixel);
23418  }
23419  }
23420  return 0;
23421 }
23422 
23423 
23427 
23442 /* start documentation of inline functions */
23443 
23493 /* end documentation of inline functions */
23494 
23499  key(0),
23500  value(0)
23501 {
23502 }
23503 
23508  key(key),
23509  value(value)
23510 {
23511 }
23512 
23513 
23517 
23553 /* start of documentation of inline functions */
23554 
23576 /* end of documentation of inline functions */
23577 
23588 QCPBars::QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis) :
23589  QCPAbstractPlottable1D<QCPBarsData>(keyAxis, valueAxis),
23590  mWidth(0.75),
23591  mWidthType(wtPlotCoords),
23592  mBarsGroup(0),
23593  mBaseValue(0),
23594  mStackingGap(0)
23595 {
23596  // modify inherited properties from abstract plottable:
23597  mPen.setColor(Qt::blue);
23598  mPen.setStyle(Qt::SolidLine);
23599  mBrush.setColor(QColor(40, 50, 255, 30));
23600  mBrush.setStyle(Qt::SolidPattern);
23601  mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255)));
23602 }
23603 
23605 {
23606  setBarsGroup(0);
23607  if (mBarBelow || mBarAbove)
23608  connectBars(mBarBelow.data(), mBarAbove.data()); // take this bar out of any stacking
23609 }
23610 
23626 void QCPBars::setData(QSharedPointer<QCPBarsDataContainer> data)
23627 {
23628  mDataContainer = data;
23629 }
23630 
23642 void QCPBars::setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
23643 {
23644  mDataContainer->clear();
23645  addData(keys, values, alreadySorted);
23646 }
23647 
23655 {
23656  mWidth = width;
23657 }
23658 
23668 {
23670 }
23671 
23679 {
23680  // deregister at old group:
23681  if (mBarsGroup)
23682  mBarsGroup->unregisterBars(this);
23684  // register at new group:
23685  if (mBarsGroup)
23686  mBarsGroup->registerBars(this);
23687 }
23688 
23702 {
23704 }
23705 
23711 void QCPBars::setStackingGap(double pixels)
23712 {
23713  mStackingGap = pixels;
23714 }
23715 
23728 void QCPBars::addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted)
23729 {
23730  if (keys.size() != values.size())
23731  qDebug() << Q_FUNC_INFO << "keys and values have different sizes:" << keys.size() << values.size();
23732  const int n = qMin(keys.size(), values.size());
23733  QVector<QCPBarsData> tempData(n);
23734  QVector<QCPBarsData>::iterator it = tempData.begin();
23735  const QVector<QCPBarsData>::iterator itEnd = tempData.end();
23736  int i = 0;
23737  while (it != itEnd)
23738  {
23739  it->key = keys[i];
23740  it->value = values[i];
23741  ++it;
23742  ++i;
23743  }
23744  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
23745 }
23746 
23753 void QCPBars::addData(double key, double value)
23754 {
23755  mDataContainer->add(QCPBarsData(key, value));
23756 }
23757 
23773 {
23774  if (bars == this) return;
23775  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
23776  {
23777  qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
23778  return;
23779  }
23780  // remove from stacking:
23781  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
23782  // if new bar given, insert this bar below it:
23783  if (bars)
23784  {
23785  if (bars->mBarBelow)
23786  connectBars(bars->mBarBelow.data(), this);
23787  connectBars(this, bars);
23788  }
23789 }
23790 
23806 {
23807  if (bars == this) return;
23808  if (bars && (bars->keyAxis() != mKeyAxis.data() || bars->valueAxis() != mValueAxis.data()))
23809  {
23810  qDebug() << Q_FUNC_INFO << "passed QCPBars* doesn't have same key and value axis as this QCPBars";
23811  return;
23812  }
23813  // remove from stacking:
23814  connectBars(mBarBelow.data(), mBarAbove.data()); // Note: also works if one (or both) of them is 0
23815  // if new bar given, insert this bar above it:
23816  if (bars)
23817  {
23818  if (bars->mBarAbove)
23819  connectBars(this, bars->mBarAbove.data());
23820  connectBars(bars, this);
23821  }
23822 }
23823 
23827 QCPDataSelection QCPBars::selectTestRect(const QRectF &rect, bool onlySelectable) const
23828 {
23829  QCPDataSelection result;
23830  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
23831  return result;
23832  if (!mKeyAxis || !mValueAxis)
23833  return result;
23834 
23835  QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
23836  getVisibleDataBounds(visibleBegin, visibleEnd);
23837 
23838  for (QCPBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
23839  {
23840  if (rect.intersects(getBarRect(it->key, it->value)))
23841  result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
23842  }
23843  result.simplify();
23844  return result;
23845 }
23846 
23847 /* inherits documentation from base class */
23848 double QCPBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
23849 {
23850  Q_UNUSED(details)
23851  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
23852  return -1;
23853  if (!mKeyAxis || !mValueAxis)
23854  return -1;
23855 
23856  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
23857  {
23858  // get visible data range:
23859  QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
23860  getVisibleDataBounds(visibleBegin, visibleEnd);
23861  for (QCPBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
23862  {
23863  if (getBarRect(it->key, it->value).contains(pos))
23864  {
23865  if (details)
23866  {
23867  int pointIndex = it-mDataContainer->constBegin();
23868  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
23869  }
23870  return mParentPlot->selectionTolerance()*0.99;
23871  }
23872  }
23873  }
23874  return -1;
23875 }
23876 
23877 /* inherits documentation from base class */
23878 QCPRange QCPBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
23879 {
23880  /* Note: If this QCPBars uses absolute pixels as width (or is in a QCPBarsGroup with spacing in
23881  absolute pixels), using this method to adapt the key axis range to fit the bars into the
23882  currently visible axis range will not work perfectly. Because in the moment the axis range is
23883  changed to the new range, the fixed pixel widths/spacings will represent different coordinate
23884  spans than before, which in turn would require a different key range to perfectly fit, and so on.
23885  The only solution would be to iteratively approach the perfect fitting axis range, but the
23886  mismatch isn't large enough in most applications, to warrant this here. If a user does need a
23887  better fit, he should call the corresponding axis rescale multiple times in a row.
23888  */
23889  QCPRange range;
23890  range = mDataContainer->keyRange(foundRange, inSignDomain);
23891 
23892  // determine exact range of bars by including bar width and barsgroup offset:
23893  if (foundRange && mKeyAxis)
23894  {
23895  double lowerPixelWidth, upperPixelWidth, keyPixel;
23896  // lower range bound:
23897  getPixelWidth(range.lower, lowerPixelWidth, upperPixelWidth);
23898  keyPixel = mKeyAxis.data()->coordToPixel(range.lower) + lowerPixelWidth;
23899  if (mBarsGroup)
23900  keyPixel += mBarsGroup->keyPixelOffset(this, range.lower);
23901  const double lowerCorrected = mKeyAxis.data()->pixelToCoord(keyPixel);
23902  if (!qIsNaN(lowerCorrected) && qIsFinite(lowerCorrected) && range.lower > lowerCorrected)
23903  range.lower = lowerCorrected;
23904  // upper range bound:
23905  getPixelWidth(range.upper, lowerPixelWidth, upperPixelWidth);
23906  keyPixel = mKeyAxis.data()->coordToPixel(range.upper) + upperPixelWidth;
23907  if (mBarsGroup)
23908  keyPixel += mBarsGroup->keyPixelOffset(this, range.upper);
23909  const double upperCorrected = mKeyAxis.data()->pixelToCoord(keyPixel);
23910  if (!qIsNaN(upperCorrected) && qIsFinite(upperCorrected) && range.upper < upperCorrected)
23911  range.upper = upperCorrected;
23912  }
23913  return range;
23914 }
23915 
23916 /* inherits documentation from base class */
23917 QCPRange QCPBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
23918 {
23919  // Note: can't simply use mDataContainer->valueRange here because we need to
23920  // take into account bar base value and possible stacking of multiple bars
23921  QCPRange range;
23922  range.lower = mBaseValue;
23923  range.upper = mBaseValue;
23924  bool haveLower = true; // set to true, because baseValue should always be visible in bar charts
23925  bool haveUpper = true; // set to true, because baseValue should always be visible in bar charts
23926  QCPBarsDataContainer::const_iterator itBegin = mDataContainer->constBegin();
23928  if (inKeyRange != QCPRange())
23929  {
23930  itBegin = mDataContainer->findBegin(inKeyRange.lower);
23931  itEnd = mDataContainer->findEnd(inKeyRange.upper);
23932  }
23933  for (QCPBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it)
23934  {
23935  const double current = it->value + getStackedBaseValue(it->key, it->value >= 0);
23936  if (qIsNaN(current)) continue;
23937  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
23938  {
23939  if (current < range.lower || !haveLower)
23940  {
23941  range.lower = current;
23942  haveLower = true;
23943  }
23944  if (current > range.upper || !haveUpper)
23945  {
23946  range.upper = current;
23947  haveUpper = true;
23948  }
23949  }
23950  }
23951 
23952  foundRange = true; // return true because bar charts always have the 0-line visible
23953  return range;
23954 }
23955 
23956 /* inherits documentation from base class */
23957 QPointF QCPBars::dataPixelPosition(int index) const
23958 {
23959  if (index >= 0 && index < mDataContainer->size())
23960  {
23961  QCPAxis *keyAxis = mKeyAxis.data();
23962  QCPAxis *valueAxis = mValueAxis.data();
23963  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QPointF(); }
23964 
23965  const QCPDataContainer<QCPBarsData>::const_iterator it = mDataContainer->constBegin()+index;
23966  const double valuePixel = valueAxis->coordToPixel(getStackedBaseValue(it->key, it->value >= 0) + it->value);
23967  const double keyPixel = keyAxis->coordToPixel(it->key) + (mBarsGroup ? mBarsGroup->keyPixelOffset(this, it->key) : 0);
23968  if (keyAxis->orientation() == Qt::Horizontal)
23969  return QPointF(keyPixel, valuePixel);
23970  else
23971  return QPointF(valuePixel, keyPixel);
23972  } else
23973  {
23974  qDebug() << Q_FUNC_INFO << "Index out of bounds" << index;
23975  return QPointF();
23976  }
23977 }
23978 
23979 /* inherits documentation from base class */
23981 {
23982  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
23983  if (mDataContainer->isEmpty()) return;
23984 
23985  QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
23986  getVisibleDataBounds(visibleBegin, visibleEnd);
23987 
23988  // loop over and draw segments of unselected/selected data:
23989  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
23990  getDataSegments(selectedSegments, unselectedSegments);
23991  allSegments << unselectedSegments << selectedSegments;
23992  for (int i=0; i<allSegments.size(); ++i)
23993  {
23994  bool isSelectedSegment = i >= unselectedSegments.size();
23995  QCPBarsDataContainer::const_iterator begin = visibleBegin;
23996  QCPBarsDataContainer::const_iterator end = visibleEnd;
23997  mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
23998  if (begin == end)
23999  continue;
24000 
24001  for (QCPBarsDataContainer::const_iterator it=begin; it!=end; ++it)
24002  {
24003  // check data validity if flag set:
24004 #ifdef QCUSTOMPLOT_CHECK_DATA
24005  if (QCP::isInvalidData(it->key, it->value))
24006  qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "of drawn range invalid." << "Plottable name:" << name();
24007 #endif
24008  // draw bar:
24009  if (isSelectedSegment && mSelectionDecorator)
24010  {
24011  mSelectionDecorator->applyBrush(painter);
24012  mSelectionDecorator->applyPen(painter);
24013  } else
24014  {
24015  painter->setBrush(mBrush);
24016  painter->setPen(mPen);
24017  }
24019  painter->drawPolygon(getBarRect(it->key, it->value));
24020  }
24021  }
24022 
24023  // draw other selection decoration that isn't just line/scatter pens and brushes:
24024  if (mSelectionDecorator)
24026 }
24027 
24028 /* inherits documentation from base class */
24029 void QCPBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
24030 {
24031  // draw filled rect:
24033  painter->setBrush(mBrush);
24034  painter->setPen(mPen);
24035  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
24036  r.moveCenter(rect.center());
24037  painter->drawRect(r);
24038 }
24039 
24055 {
24056  if (!mKeyAxis)
24057  {
24058  qDebug() << Q_FUNC_INFO << "invalid key axis";
24059  begin = mDataContainer->constEnd();
24060  end = mDataContainer->constEnd();
24061  return;
24062  }
24063  if (mDataContainer->isEmpty())
24064  {
24065  begin = mDataContainer->constEnd();
24066  end = mDataContainer->constEnd();
24067  return;
24068  }
24069 
24070  // get visible data range as QMap iterators
24071  begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower);
24072  end = mDataContainer->findEnd(mKeyAxis.data()->range().upper);
24073  double lowerPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().lower);
24074  double upperPixelBound = mKeyAxis.data()->coordToPixel(mKeyAxis.data()->range().upper);
24075  bool isVisible = false;
24076  // walk left from begin to find lower bar that actually is completely outside visible pixel range:
24078  while (it != mDataContainer->constBegin())
24079  {
24080  --it;
24081  const QRectF barRect = getBarRect(it->key, it->value);
24082  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
24083  isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.right() >= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.left() <= lowerPixelBound));
24084  else // keyaxis is vertical
24085  isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.top() <= lowerPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.bottom() >= lowerPixelBound));
24086  if (isVisible)
24087  begin = it;
24088  else
24089  break;
24090  }
24091  // walk right from ubound to find upper bar that actually is completely outside visible pixel range:
24092  it = end;
24093  while (it != mDataContainer->constEnd())
24094  {
24095  const QRectF barRect = getBarRect(it->key, it->value);
24096  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
24097  isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.left() <= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.right() >= upperPixelBound));
24098  else // keyaxis is vertical
24099  isVisible = ((!mKeyAxis.data()->rangeReversed() && barRect.bottom() >= upperPixelBound) || (mKeyAxis.data()->rangeReversed() && barRect.top() <= upperPixelBound));
24100  if (isVisible)
24101  end = it+1;
24102  else
24103  break;
24104  ++it;
24105  }
24106 }
24107 
24114 QRectF QCPBars::getBarRect(double key, double value) const
24115 {
24116  QCPAxis *keyAxis = mKeyAxis.data();
24117  QCPAxis *valueAxis = mValueAxis.data();
24118  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QRectF(); }
24119 
24120  double lowerPixelWidth, upperPixelWidth;
24121  getPixelWidth(key, lowerPixelWidth, upperPixelWidth);
24122  double base = getStackedBaseValue(key, value >= 0);
24123  double basePixel = valueAxis->coordToPixel(base);
24124  double valuePixel = valueAxis->coordToPixel(base+value);
24125  double keyPixel = keyAxis->coordToPixel(key);
24126  if (mBarsGroup)
24127  keyPixel += mBarsGroup->keyPixelOffset(this, key);
24128  double bottomOffset = (mBarBelow && mPen != Qt::NoPen ? 1 : 0)*(mPen.isCosmetic() ? 1 : mPen.widthF());
24129  bottomOffset += mBarBelow ? mStackingGap : 0;
24130  bottomOffset *= (value<0 ? -1 : 1)*valueAxis->pixelOrientation();
24131  if (qAbs(valuePixel-basePixel) <= qAbs(bottomOffset))
24132  bottomOffset = valuePixel-basePixel;
24133  if (keyAxis->orientation() == Qt::Horizontal)
24134  {
24135  return QRectF(QPointF(keyPixel+lowerPixelWidth, valuePixel), QPointF(keyPixel+upperPixelWidth, basePixel+bottomOffset)).normalized();
24136  } else
24137  {
24138  return QRectF(QPointF(basePixel+bottomOffset, keyPixel+lowerPixelWidth), QPointF(valuePixel, keyPixel+upperPixelWidth)).normalized();
24139  }
24140 }
24141 
24151 void QCPBars::getPixelWidth(double key, double &lower, double &upper) const
24152 {
24153  lower = 0;
24154  upper = 0;
24155  switch (mWidthType)
24156  {
24157  case wtAbsolute:
24158  {
24159  upper = mWidth*0.5*mKeyAxis.data()->pixelOrientation();
24160  lower = -upper;
24161  break;
24162  }
24163  case wtAxisRectRatio:
24164  {
24165  if (mKeyAxis && mKeyAxis.data()->axisRect())
24166  {
24167  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
24168  upper = mKeyAxis.data()->axisRect()->width()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
24169  else
24170  upper = mKeyAxis.data()->axisRect()->height()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
24171  lower = -upper;
24172  } else
24173  qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
24174  break;
24175  }
24176  case wtPlotCoords:
24177  {
24178  if (mKeyAxis)
24179  {
24180  double keyPixel = mKeyAxis.data()->coordToPixel(key);
24181  upper = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
24182  lower = mKeyAxis.data()->coordToPixel(key-mWidth*0.5)-keyPixel;
24183  // no need to qSwap(lower, higher) when range reversed, because higher/lower are gained by
24184  // coordinate transform which includes range direction
24185  } else
24186  qDebug() << Q_FUNC_INFO << "No key axis defined";
24187  break;
24188  }
24189  }
24190 }
24191 
24201 double QCPBars::getStackedBaseValue(double key, bool positive) const
24202 {
24203  if (mBarBelow)
24204  {
24205  double max = 0; // don't initialize with mBaseValue here because only base value of bottom-most bar has meaning in a bar stack
24206  // find bars of mBarBelow that are approximately at key and find largest one:
24207  double epsilon = qAbs(key)*(sizeof(key)==4 ? 1e-6 : 1e-14); // should be safe even when changed to use float at some point
24208  if (key == 0)
24209  epsilon = (sizeof(key)==4 ? 1e-6 : 1e-14);
24210  QCPBarsDataContainer::const_iterator it = mBarBelow.data()->mDataContainer->findBegin(key-epsilon);
24211  QCPBarsDataContainer::const_iterator itEnd = mBarBelow.data()->mDataContainer->findEnd(key+epsilon);
24212  while (it != itEnd)
24213  {
24214  if (it->key > key-epsilon && it->key < key+epsilon)
24215  {
24216  if ((positive && it->value > max) ||
24217  (!positive && it->value < max))
24218  max = it->value;
24219  }
24220  ++it;
24221  }
24222  // recurse down the bar-stack to find the total height:
24223  return max + mBarBelow.data()->getStackedBaseValue(key, positive);
24224  } else
24225  return mBaseValue;
24226 }
24227 
24237 {
24238  if (!lower && !upper) return;
24239 
24240  if (!lower) // disconnect upper at bottom
24241  {
24242  // disconnect old bar below upper:
24243  if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
24244  upper->mBarBelow.data()->mBarAbove = 0;
24245  upper->mBarBelow = 0;
24246  } else if (!upper) // disconnect lower at top
24247  {
24248  // disconnect old bar above lower:
24249  if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
24250  lower->mBarAbove.data()->mBarBelow = 0;
24251  lower->mBarAbove = 0;
24252  } else // connect lower and upper
24253  {
24254  // disconnect old bar above lower:
24255  if (lower->mBarAbove && lower->mBarAbove.data()->mBarBelow.data() == lower)
24256  lower->mBarAbove.data()->mBarBelow = 0;
24257  // disconnect old bar below upper:
24258  if (upper->mBarBelow && upper->mBarBelow.data()->mBarAbove.data() == upper)
24259  upper->mBarBelow.data()->mBarAbove = 0;
24260  lower->mBarAbove = upper;
24261  upper->mBarBelow = lower;
24262  }
24263 }
24264 /* end of 'src/plottables/plottable-bars.cpp' */
24265 
24266 
24267 /* including file 'src/plottables/plottable-statisticalbox.cpp', size 28622 */
24268 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
24269 
24273 
24309 /* start documentation of inline functions */
24310 
24361 /* end documentation of inline functions */
24362 
24367  key(0),
24368  minimum(0),
24369  lowerQuartile(0),
24370  median(0),
24371  upperQuartile(0),
24372  maximum(0)
24373 {
24374 }
24375 
24380 QCPStatisticalBoxData::QCPStatisticalBoxData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector<double> &outliers) :
24381  key(key),
24382  minimum(minimum),
24383  lowerQuartile(lowerQuartile),
24384  median(median),
24385  upperQuartile(upperQuartile),
24386  maximum(maximum),
24387  outliers(outliers)
24388 {
24389 }
24390 
24391 
24395 
24444 /* start documentation of inline functions */
24445 
24453 /* end documentation of inline functions */
24454 
24466  QCPAbstractPlottable1D<QCPStatisticalBoxData>(keyAxis, valueAxis),
24467  mWidth(0.5),
24468  mWhiskerWidth(0.2),
24469  mWhiskerPen(Qt::black, 0, Qt::DashLine, Qt::FlatCap),
24470  mWhiskerBarPen(Qt::black),
24471  mWhiskerAntialiased(false),
24472  mMedianPen(Qt::black, 3, Qt::SolidLine, Qt::FlatCap),
24473  mOutlierStyle(QCPScatterStyle::ssCircle, Qt::blue, 6)
24474 {
24475  setPen(QPen(Qt::black));
24476  setBrush(Qt::NoBrush);
24477 }
24478 
24495 void QCPStatisticalBox::setData(QSharedPointer<QCPStatisticalBoxDataContainer> data)
24496 {
24497  mDataContainer = data;
24498 }
24510 void QCPStatisticalBox::setData(const QVector<double> &keys, const QVector<double> &minimum, const QVector<double> &lowerQuartile, const QVector<double> &median, const QVector<double> &upperQuartile, const QVector<double> &maximum, bool alreadySorted)
24511 {
24512  mDataContainer->clear();
24513  addData(keys, minimum, lowerQuartile, median, upperQuartile, maximum, alreadySorted);
24514 }
24515 
24522 {
24523  mWidth = width;
24524 }
24525 
24535 {
24536  mWhiskerWidth = width;
24537 }
24538 
24551 {
24552  mWhiskerPen = pen;
24553 }
24554 
24565 {
24566  mWhiskerBarPen = pen;
24567 }
24568 
24576 {
24577  mWhiskerAntialiased = enabled;
24578 }
24579 
24584 {
24585  mMedianPen = pen;
24586 }
24587 
24595 {
24596  mOutlierStyle = style;
24597 }
24598 
24611 void QCPStatisticalBox::addData(const QVector<double> &keys, const QVector<double> &minimum, const QVector<double> &lowerQuartile, const QVector<double> &median, const QVector<double> &upperQuartile, const QVector<double> &maximum, bool alreadySorted)
24612 {
24613  if (keys.size() != minimum.size() || minimum.size() != lowerQuartile.size() || lowerQuartile.size() != median.size() ||
24614  median.size() != upperQuartile.size() || upperQuartile.size() != maximum.size() || maximum.size() != keys.size())
24615  qDebug() << Q_FUNC_INFO << "keys, minimum, lowerQuartile, median, upperQuartile, maximum have different sizes:"
24616  << keys.size() << minimum.size() << lowerQuartile.size() << median.size() << upperQuartile.size() << maximum.size();
24617  const int n = qMin(keys.size(), qMin(minimum.size(), qMin(lowerQuartile.size(), qMin(median.size(), qMin(upperQuartile.size(), maximum.size())))));
24618  QVector<QCPStatisticalBoxData> tempData(n);
24619  QVector<QCPStatisticalBoxData>::iterator it = tempData.begin();
24620  const QVector<QCPStatisticalBoxData>::iterator itEnd = tempData.end();
24621  int i = 0;
24622  while (it != itEnd)
24623  {
24624  it->key = keys[i];
24625  it->minimum = minimum[i];
24626  it->lowerQuartile = lowerQuartile[i];
24627  it->median = median[i];
24628  it->upperQuartile = upperQuartile[i];
24629  it->maximum = maximum[i];
24630  ++it;
24631  ++i;
24632  }
24633  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
24634 }
24635 
24644 void QCPStatisticalBox::addData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector<double> &outliers)
24645 {
24646  mDataContainer->add(QCPStatisticalBoxData(key, minimum, lowerQuartile, median, upperQuartile, maximum, outliers));
24647 }
24648 
24652 QCPDataSelection QCPStatisticalBox::selectTestRect(const QRectF &rect, bool onlySelectable) const
24653 {
24654  QCPDataSelection result;
24655  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
24656  return result;
24657  if (!mKeyAxis || !mValueAxis)
24658  return result;
24659 
24660  QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
24661  getVisibleDataBounds(visibleBegin, visibleEnd);
24662 
24663  for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
24664  {
24665  if (rect.intersects(getQuartileBox(it)))
24666  result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
24667  }
24668  result.simplify();
24669  return result;
24670 }
24671 
24672 /* inherits documentation from base class */
24673 double QCPStatisticalBox::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
24674 {
24675  Q_UNUSED(details)
24676  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
24677  return -1;
24678  if (!mKeyAxis || !mValueAxis)
24679  return -1;
24680 
24681  if (mKeyAxis->axisRect()->rect().contains(pos.toPoint()))
24682  {
24683  // get visible data range:
24684  QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
24685  QCPStatisticalBoxDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
24686  getVisibleDataBounds(visibleBegin, visibleEnd);
24687  double minDistSqr = std::numeric_limits<double>::max();
24688  for (QCPStatisticalBoxDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
24689  {
24690  if (getQuartileBox(it).contains(pos)) // quartile box
24691  {
24692  double currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
24693  if (currentDistSqr < minDistSqr)
24694  {
24695  minDistSqr = currentDistSqr;
24696  closestDataPoint = it;
24697  }
24698  } else // whiskers
24699  {
24700  const QVector<QLineF> whiskerBackbones(getWhiskerBackboneLines(it));
24701  for (int i=0; i<whiskerBackbones.size(); ++i)
24702  {
24703  double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(whiskerBackbones.at(i));
24704  if (currentDistSqr < minDistSqr)
24705  {
24706  minDistSqr = currentDistSqr;
24707  closestDataPoint = it;
24708  }
24709  }
24710  }
24711  }
24712  if (details)
24713  {
24714  int pointIndex = closestDataPoint-mDataContainer->constBegin();
24715  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
24716  }
24717  return qSqrt(minDistSqr);
24718  }
24719  return -1;
24720 }
24721 
24722 /* inherits documentation from base class */
24723 QCPRange QCPStatisticalBox::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
24724 {
24725  QCPRange range = mDataContainer->keyRange(foundRange, inSignDomain);
24726  // determine exact range by including width of bars/flags:
24727  if (foundRange)
24728  {
24729  if (inSignDomain != QCP::sdPositive || range.lower-mWidth*0.5 > 0)
24730  range.lower -= mWidth*0.5;
24731  if (inSignDomain != QCP::sdNegative || range.upper+mWidth*0.5 < 0)
24732  range.upper += mWidth*0.5;
24733  }
24734  return range;
24735 }
24736 
24737 /* inherits documentation from base class */
24738 QCPRange QCPStatisticalBox::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
24739 {
24740  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
24741 }
24742 
24743 /* inherits documentation from base class */
24745 {
24746  if (mDataContainer->isEmpty()) return;
24747  QCPAxis *keyAxis = mKeyAxis.data();
24748  QCPAxis *valueAxis = mValueAxis.data();
24749  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
24750 
24751  QCPStatisticalBoxDataContainer::const_iterator visibleBegin, visibleEnd;
24752  getVisibleDataBounds(visibleBegin, visibleEnd);
24753 
24754  // loop over and draw segments of unselected/selected data:
24755  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
24756  getDataSegments(selectedSegments, unselectedSegments);
24757  allSegments << unselectedSegments << selectedSegments;
24758  for (int i=0; i<allSegments.size(); ++i)
24759  {
24760  bool isSelectedSegment = i >= unselectedSegments.size();
24763  mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
24764  if (begin == end)
24765  continue;
24766 
24767  for (QCPStatisticalBoxDataContainer::const_iterator it=begin; it!=end; ++it)
24768  {
24769  // check data validity if flag set:
24770 # ifdef QCUSTOMPLOT_CHECK_DATA
24771  if (QCP::isInvalidData(it->key, it->minimum) ||
24772  QCP::isInvalidData(it->lowerQuartile, it->median) ||
24773  QCP::isInvalidData(it->upperQuartile, it->maximum))
24774  qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "of drawn range has invalid data." << "Plottable name:" << name();
24775  for (int i=0; i<it->outliers.size(); ++i)
24776  if (QCP::isInvalidData(it->outliers.at(i)))
24777  qDebug() << Q_FUNC_INFO << "Data point outlier at" << it->key << "of drawn range invalid." << "Plottable name:" << name();
24778 # endif
24779 
24780  if (isSelectedSegment && mSelectionDecorator)
24781  {
24782  mSelectionDecorator->applyPen(painter);
24783  mSelectionDecorator->applyBrush(painter);
24784  } else
24785  {
24786  painter->setPen(mPen);
24787  painter->setBrush(mBrush);
24788  }
24789  QCPScatterStyle finalOutlierStyle = mOutlierStyle;
24790  if (isSelectedSegment && mSelectionDecorator)
24792  drawStatisticalBox(painter, it, finalOutlierStyle);
24793  }
24794  }
24795 
24796  // draw other selection decoration that isn't just line/scatter pens and brushes:
24797  if (mSelectionDecorator)
24799 }
24800 
24801 /* inherits documentation from base class */
24802 void QCPStatisticalBox::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
24803 {
24804  // draw filled rect:
24806  painter->setPen(mPen);
24807  painter->setBrush(mBrush);
24808  QRectF r = QRectF(0, 0, rect.width()*0.67, rect.height()*0.67);
24809  r.moveCenter(rect.center());
24810  painter->drawRect(r);
24811 }
24812 
24822 {
24823  // draw quartile box:
24825  const QRectF quartileBox = getQuartileBox(it);
24826  painter->drawRect(quartileBox);
24827  // draw median line with cliprect set to quartile box:
24828  painter->save();
24829  painter->setClipRect(quartileBox, Qt::IntersectClip);
24830  painter->setPen(mMedianPen);
24831  painter->drawLine(QLineF(coordsToPixels(it->key-mWidth*0.5, it->median), coordsToPixels(it->key+mWidth*0.5, it->median)));
24832  painter->restore();
24833  // draw whisker lines:
24835  painter->setPen(mWhiskerPen);
24836  painter->drawLines(getWhiskerBackboneLines(it));
24837  painter->setPen(mWhiskerBarPen);
24838  painter->drawLines(getWhiskerBarLines(it));
24839  // draw outliers:
24841  outlierStyle.applyTo(painter, mPen);
24842  for (int i=0; i<it->outliers.size(); ++i)
24843  outlierStyle.drawShape(painter, coordsToPixels(it->key, it->outliers.at(i)));
24844 }
24845 
24861 {
24862  if (!mKeyAxis)
24863  {
24864  qDebug() << Q_FUNC_INFO << "invalid key axis";
24865  begin = mDataContainer->constEnd();
24866  end = mDataContainer->constEnd();
24867  return;
24868  }
24869  begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower-mWidth*0.5); // subtract half width of box to include partially visible data points
24870  end = mDataContainer->findEnd(mKeyAxis.data()->range().upper+mWidth*0.5); // add half width of box to include partially visible data points
24871 }
24872 
24881 {
24882  QRectF result;
24883  result.setTopLeft(coordsToPixels(it->key-mWidth*0.5, it->upperQuartile));
24884  result.setBottomRight(coordsToPixels(it->key+mWidth*0.5, it->lowerQuartile));
24885  return result;
24886 }
24887 
24897 {
24898  QVector<QLineF> result(2);
24899  result[0].setPoints(coordsToPixels(it->key, it->lowerQuartile), coordsToPixels(it->key, it->minimum)); // min backbone
24900  result[1].setPoints(coordsToPixels(it->key, it->upperQuartile), coordsToPixels(it->key, it->maximum)); // max backbone
24901  return result;
24902 }
24903 
24912 {
24913  QVector<QLineF> result(2);
24914  result[0].setPoints(coordsToPixels(it->key-mWhiskerWidth*0.5, it->minimum), coordsToPixels(it->key+mWhiskerWidth*0.5, it->minimum)); // min bar
24915  result[1].setPoints(coordsToPixels(it->key-mWhiskerWidth*0.5, it->maximum), coordsToPixels(it->key+mWhiskerWidth*0.5, it->maximum)); // max bar
24916  return result;
24917 }
24918 /* end of 'src/plottables/plottable-statisticalbox.cpp' */
24919 
24920 
24921 /* including file 'src/plottables/plottable-colormap.cpp', size 47881 */
24922 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
24923 
24927 
24961 /* start of documentation of inline functions */
24962 
24969 /* end of documentation of inline functions */
24970 
24978 QCPColorMapData::QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange) :
24979  mKeySize(0),
24980  mValueSize(0),
24981  mKeyRange(keyRange),
24982  mValueRange(valueRange),
24983  mIsEmpty(true),
24984  mData(0),
24985  mAlpha(0),
24986  mDataModified(true)
24987 {
24988  setSize(keySize, valueSize);
24989  fill(0);
24990 }
24991 
24993 {
24994  if (mData)
24995  delete[] mData;
24996  if (mAlpha)
24997  delete[] mAlpha;
24998 }
24999 
25004  mKeySize(0),
25005  mValueSize(0),
25006  mIsEmpty(true),
25007  mData(0),
25008  mAlpha(0),
25009  mDataModified(true)
25010 {
25011  *this = other;
25012 }
25013 
25019 {
25020  if (&other != this)
25021  {
25022  const int keySize = other.keySize();
25023  const int valueSize = other.valueSize();
25024  if (!other.mAlpha && mAlpha)
25025  clearAlpha();
25026  setSize(keySize, valueSize);
25027  if (other.mAlpha && !mAlpha)
25028  createAlpha(false);
25029  setRange(other.keyRange(), other.valueRange());
25030  if (!isEmpty())
25031  {
25032  memcpy(mData, other.mData, sizeof(mData[0])*keySize*valueSize);
25033  if (mAlpha)
25034  memcpy(mAlpha, other.mAlpha, sizeof(mAlpha[0])*keySize*valueSize);
25035  }
25036  mDataBounds = other.mDataBounds;
25037  mDataModified = true;
25038  }
25039  return *this;
25040 }
25041 
25042 /* undocumented getter */
25043 double QCPColorMapData::data(double key, double value)
25044 {
25045  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
25046  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
25047  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
25048  return mData[valueCell*mKeySize + keyCell];
25049  else
25050  return 0;
25051 }
25052 
25053 /* undocumented getter */
25054 double QCPColorMapData::cell(int keyIndex, int valueIndex)
25055 {
25056  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
25057  return mData[valueIndex*mKeySize + keyIndex];
25058  else
25059  return 0;
25060 }
25061 
25070 unsigned char QCPColorMapData::alpha(int keyIndex, int valueIndex)
25071 {
25072  if (mAlpha && keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
25073  return mAlpha[valueIndex*mKeySize + keyIndex];
25074  else
25075  return 255;
25076 }
25077 
25091 {
25092  if (keySize != mKeySize || valueSize != mValueSize)
25093  {
25094  mKeySize = keySize;
25096  if (mData)
25097  delete[] mData;
25098  mIsEmpty = mKeySize == 0 || mValueSize == 0;
25099  if (!mIsEmpty)
25100  {
25101 #ifdef __EXCEPTIONS
25102  try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
25103 #endif
25104  mData = new double[mKeySize*mValueSize];
25105 #ifdef __EXCEPTIONS
25106  } catch (...) { mData = 0; }
25107 #endif
25108  if (mData)
25109  fill(0);
25110  else
25111  qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
25112  } else
25113  mData = 0;
25114 
25115  if (mAlpha) // if we had an alpha map, recreate it with new size
25116  createAlpha();
25117 
25118  mDataModified = true;
25119  }
25120 }
25121 
25133 {
25134  setSize(keySize, mValueSize);
25135 }
25136 
25148 {
25149  setSize(mKeySize, valueSize);
25150 }
25151 
25163 {
25164  setKeyRange(keyRange);
25165  setValueRange(valueRange);
25166 }
25167 
25179 {
25180  mKeyRange = keyRange;
25181 }
25182 
25194 {
25196 }
25197 
25210 void QCPColorMapData::setData(double key, double value, double z)
25211 {
25212  int keyCell = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
25213  int valueCell = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
25214  if (keyCell >= 0 && keyCell < mKeySize && valueCell >= 0 && valueCell < mValueSize)
25215  {
25216  mData[valueCell*mKeySize + keyCell] = z;
25217  if (z < mDataBounds.lower)
25218  mDataBounds.lower = z;
25219  if (z > mDataBounds.upper)
25220  mDataBounds.upper = z;
25221  mDataModified = true;
25222  }
25223 }
25224 
25236 void QCPColorMapData::setCell(int keyIndex, int valueIndex, double z)
25237 {
25238  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
25239  {
25240  mData[valueIndex*mKeySize + keyIndex] = z;
25241  if (z < mDataBounds.lower)
25242  mDataBounds.lower = z;
25243  if (z > mDataBounds.upper)
25244  mDataBounds.upper = z;
25245  mDataModified = true;
25246  } else
25247  qDebug() << Q_FUNC_INFO << "index out of bounds:" << keyIndex << valueIndex;
25248 }
25249 
25265 void QCPColorMapData::setAlpha(int keyIndex, int valueIndex, unsigned char alpha)
25266 {
25267  if (keyIndex >= 0 && keyIndex < mKeySize && valueIndex >= 0 && valueIndex < mValueSize)
25268  {
25269  if (mAlpha || createAlpha())
25270  {
25271  mAlpha[valueIndex*mKeySize + keyIndex] = alpha;
25272  mDataModified = true;
25273  }
25274  } else
25275  qDebug() << Q_FUNC_INFO << "index out of bounds:" << keyIndex << valueIndex;
25276 }
25277 
25292 {
25293  if (mKeySize > 0 && mValueSize > 0)
25294  {
25295  double minHeight = mData[0];
25296  double maxHeight = mData[0];
25297  const int dataCount = mValueSize*mKeySize;
25298  for (int i=0; i<dataCount; ++i)
25299  {
25300  if (mData[i] > maxHeight)
25301  maxHeight = mData[i];
25302  if (mData[i] < minHeight)
25303  minHeight = mData[i];
25304  }
25305  mDataBounds.lower = minHeight;
25306  mDataBounds.upper = maxHeight;
25307  }
25308 }
25309 
25316 {
25317  setSize(0, 0);
25318 }
25319 
25324 {
25325  if (mAlpha)
25326  {
25327  delete[] mAlpha;
25328  mAlpha = 0;
25329  mDataModified = true;
25330  }
25331 }
25332 
25337 {
25338  const int dataCount = mValueSize*mKeySize;
25339  for (int i=0; i<dataCount; ++i)
25340  mData[i] = z;
25341  mDataBounds = QCPRange(z, z);
25342  mDataModified = true;
25343 }
25344 
25355 {
25356  if (mAlpha || createAlpha(false))
25357  {
25358  const int dataCount = mValueSize*mKeySize;
25359  for (int i=0; i<dataCount; ++i)
25360  mAlpha[i] = alpha;
25361  mDataModified = true;
25362  }
25363 }
25364 
25382 void QCPColorMapData::coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
25383 {
25384  if (keyIndex)
25385  *keyIndex = (key-mKeyRange.lower)/(mKeyRange.upper-mKeyRange.lower)*(mKeySize-1)+0.5;
25386  if (valueIndex)
25387  *valueIndex = (value-mValueRange.lower)/(mValueRange.upper-mValueRange.lower)*(mValueSize-1)+0.5;
25388 }
25389 
25405 void QCPColorMapData::cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
25406 {
25407  if (key)
25408  *key = keyIndex/(double)(mKeySize-1)*(mKeyRange.upper-mKeyRange.lower)+mKeyRange.lower;
25409  if (value)
25410  *value = valueIndex/(double)(mValueSize-1)*(mValueRange.upper-mValueRange.lower)+mValueRange.lower;
25411 }
25412 
25426 bool QCPColorMapData::createAlpha(bool initializeOpaque)
25427 {
25428  clearAlpha();
25429  if (isEmpty())
25430  return false;
25431 
25432 #ifdef __EXCEPTIONS
25433  try { // 2D arrays get memory intensive fast. So if the allocation fails, at least output debug message
25434 #endif
25435  mAlpha = new unsigned char[mKeySize*mValueSize];
25436 #ifdef __EXCEPTIONS
25437  } catch (...) { mAlpha = 0; }
25438 #endif
25439  if (mAlpha)
25440  {
25441  if (initializeOpaque)
25442  fillAlpha(255);
25443  return true;
25444  } else
25445  {
25446  qDebug() << Q_FUNC_INFO << "out of memory for data dimensions "<< mKeySize << "*" << mValueSize;
25447  return false;
25448  }
25449 }
25450 
25451 
25455 
25531 /* start documentation of inline functions */
25532 
25541 /* end documentation of inline functions */
25542 
25543 /* start documentation of signals */
25544 
25566 /* end documentation of signals */
25567 
25576  QCPAbstractPlottable(keyAxis, valueAxis),
25577  mDataScaleType(QCPAxis::stLinear),
25578  mMapData(new QCPColorMapData(10, 10, QCPRange(0, 5), QCPRange(0, 5))),
25579  mGradient(QCPColorGradient::gpCold),
25580  mInterpolate(true),
25581  mTightBoundary(false),
25582  mMapImageInvalidated(true)
25583 {
25584 }
25585 
25587 {
25588  delete mMapData;
25589 }
25590 
25599 {
25600  if (mMapData == data)
25601  {
25602  qDebug() << Q_FUNC_INFO << "The data pointer is already in (and owned by) this plottable" << reinterpret_cast<quintptr>(data);
25603  return;
25604  }
25605  if (copy)
25606  {
25607  *mMapData = *data;
25608  } else
25609  {
25610  delete mMapData;
25611  mMapData = data;
25612  }
25613  mMapImageInvalidated = true;
25614 }
25615 
25625 {
25626  if (!QCPRange::validRange(dataRange)) return;
25627  if (mDataRange.lower != dataRange.lower || mDataRange.upper != dataRange.upper)
25628  {
25630  mDataRange = dataRange.sanitizedForLogScale();
25631  else
25632  mDataRange = dataRange.sanitizedForLinScale();
25633  mMapImageInvalidated = true;
25635  }
25636 }
25637 
25644 {
25645  if (mDataScaleType != scaleType)
25646  {
25647  mDataScaleType = scaleType;
25648  mMapImageInvalidated = true;
25652  }
25653 }
25654 
25667 {
25668  if (mGradient != gradient)
25669  {
25670  mGradient = gradient;
25671  mMapImageInvalidated = true;
25672  emit gradientChanged(mGradient);
25673  }
25674 }
25675 
25683 {
25684  mInterpolate = enabled;
25685  mMapImageInvalidated = true; // because oversampling factors might need to change
25686 }
25687 
25700 {
25701  mTightBoundary = enabled;
25702 }
25703 
25719 {
25720  if (mColorScale) // unconnect signals from old color scale
25721  {
25722  disconnect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
25723  disconnect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
25724  disconnect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
25725  disconnect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
25726  disconnect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
25727  disconnect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
25728  }
25730  if (mColorScale) // connect signals to new color scale
25731  {
25732  setGradient(mColorScale.data()->gradient());
25733  setDataRange(mColorScale.data()->dataRange());
25734  setDataScaleType(mColorScale.data()->dataScaleType());
25735  connect(this, SIGNAL(dataRangeChanged(QCPRange)), mColorScale.data(), SLOT(setDataRange(QCPRange)));
25736  connect(this, SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), mColorScale.data(), SLOT(setDataScaleType(QCPAxis::ScaleType)));
25737  connect(this, SIGNAL(gradientChanged(QCPColorGradient)), mColorScale.data(), SLOT(setGradient(QCPColorGradient)));
25738  connect(mColorScale.data(), SIGNAL(dataRangeChanged(QCPRange)), this, SLOT(setDataRange(QCPRange)));
25739  connect(mColorScale.data(), SIGNAL(gradientChanged(QCPColorGradient)), this, SLOT(setGradient(QCPColorGradient)));
25740  connect(mColorScale.data(), SIGNAL(dataScaleTypeChanged(QCPAxis::ScaleType)), this, SLOT(setDataScaleType(QCPAxis::ScaleType)));
25741  }
25742 }
25743 
25764 void QCPColorMap::rescaleDataRange(bool recalculateDataBounds)
25765 {
25766  if (recalculateDataBounds)
25769 }
25770 
25785 void QCPColorMap::updateLegendIcon(Qt::TransformationMode transformMode, const QSize &thumbSize)
25786 {
25787  if (mMapImage.isNull() && !data()->isEmpty())
25788  updateMapImage(); // try to update map image if it's null (happens if no draw has happened yet)
25789 
25790  if (!mMapImage.isNull()) // might still be null, e.g. if data is empty, so check here again
25791  {
25792  bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
25793  bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
25794  mLegendIcon = QPixmap::fromImage(mMapImage.mirrored(mirrorX, mirrorY)).scaled(thumbSize, Qt::KeepAspectRatio, transformMode);
25795  }
25796 }
25797 
25798 /* inherits documentation from base class */
25799 double QCPColorMap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
25800 {
25801  Q_UNUSED(details)
25802  if ((onlySelectable && mSelectable == QCP::stNone) || mMapData->isEmpty())
25803  return -1;
25804  if (!mKeyAxis || !mValueAxis)
25805  return -1;
25806 
25807  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
25808  {
25809  double posKey, posValue;
25810  pixelsToCoords(pos, posKey, posValue);
25811  if (mMapData->keyRange().contains(posKey) && mMapData->valueRange().contains(posValue))
25812  {
25813  if (details)
25814  details->setValue(QCPDataSelection(QCPDataRange(0, 1))); // temporary solution, to facilitate whole-plottable selection. Replace in future version with segmented 2D selection.
25815  return mParentPlot->selectionTolerance()*0.99;
25816  }
25817  }
25818  return -1;
25819 }
25820 
25821 /* inherits documentation from base class */
25822 QCPRange QCPColorMap::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
25823 {
25824  foundRange = true;
25825  QCPRange result = mMapData->keyRange();
25826  result.normalize();
25827  if (inSignDomain == QCP::sdPositive)
25828  {
25829  if (result.lower <= 0 && result.upper > 0)
25830  result.lower = result.upper*1e-3;
25831  else if (result.lower <= 0 && result.upper <= 0)
25832  foundRange = false;
25833  } else if (inSignDomain == QCP::sdNegative)
25834  {
25835  if (result.upper >= 0 && result.lower < 0)
25836  result.upper = result.lower*1e-3;
25837  else if (result.upper >= 0 && result.lower >= 0)
25838  foundRange = false;
25839  }
25840  return result;
25841 }
25842 
25843 /* inherits documentation from base class */
25844 QCPRange QCPColorMap::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
25845 {
25846  if (inKeyRange != QCPRange())
25847  {
25848  if (mMapData->keyRange().upper < inKeyRange.lower || mMapData->keyRange().lower > inKeyRange.upper)
25849  {
25850  foundRange = false;
25851  return QCPRange();
25852  }
25853  }
25854 
25855  foundRange = true;
25856  QCPRange result = mMapData->valueRange();
25857  result.normalize();
25858  if (inSignDomain == QCP::sdPositive)
25859  {
25860  if (result.lower <= 0 && result.upper > 0)
25861  result.lower = result.upper*1e-3;
25862  else if (result.lower <= 0 && result.upper <= 0)
25863  foundRange = false;
25864  } else if (inSignDomain == QCP::sdNegative)
25865  {
25866  if (result.upper >= 0 && result.lower < 0)
25867  result.upper = result.lower*1e-3;
25868  else if (result.upper >= 0 && result.lower >= 0)
25869  foundRange = false;
25870  }
25871  return result;
25872 }
25873 
25889 {
25890  QCPAxis *keyAxis = mKeyAxis.data();
25891  if (!keyAxis) return;
25892  if (mMapData->isEmpty()) return;
25893 
25894  const QImage::Format format = QImage::Format_ARGB32_Premultiplied;
25895  const int keySize = mMapData->keySize();
25896  const int valueSize = mMapData->valueSize();
25897  int keyOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)keySize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on
25898  int valueOversamplingFactor = mInterpolate ? 1 : (int)(1.0+100.0/(double)valueSize); // make mMapImage have at least size 100, factor becomes 1 if size > 200 or interpolation is on
25899 
25900  // resize mMapImage to correct dimensions including possible oversampling factors, according to key/value axes orientation:
25901  if (keyAxis->orientation() == Qt::Horizontal && (mMapImage.width() != keySize*keyOversamplingFactor || mMapImage.height() != valueSize*valueOversamplingFactor))
25902  mMapImage = QImage(QSize(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor), format);
25903  else if (keyAxis->orientation() == Qt::Vertical && (mMapImage.width() != valueSize*valueOversamplingFactor || mMapImage.height() != keySize*keyOversamplingFactor))
25904  mMapImage = QImage(QSize(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor), format);
25905 
25906  if (mMapImage.isNull())
25907  {
25908  qDebug() << Q_FUNC_INFO << "Couldn't create map image (possibly too large for memory)";
25909  mMapImage = QImage(QSize(10, 10), format);
25910  mMapImage.fill(Qt::black);
25911  } else
25912  {
25913  QImage *localMapImage = &mMapImage; // this is the image on which the colorization operates. Either the final mMapImage, or if we need oversampling, mUndersampledMapImage
25914  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
25915  {
25916  // resize undersampled map image to actual key/value cell sizes:
25917  if (keyAxis->orientation() == Qt::Horizontal && (mUndersampledMapImage.width() != keySize || mUndersampledMapImage.height() != valueSize))
25918  mUndersampledMapImage = QImage(QSize(keySize, valueSize), format);
25919  else if (keyAxis->orientation() == Qt::Vertical && (mUndersampledMapImage.width() != valueSize || mUndersampledMapImage.height() != keySize))
25920  mUndersampledMapImage = QImage(QSize(valueSize, keySize), format);
25921  localMapImage = &mUndersampledMapImage; // make the colorization run on the undersampled image
25922  } else if (!mUndersampledMapImage.isNull())
25923  mUndersampledMapImage = QImage(); // don't need oversampling mechanism anymore (map size has changed) but mUndersampledMapImage still has nonzero size, free it
25924 
25925  const double *rawData = mMapData->mData;
25926  const unsigned char *rawAlpha = mMapData->mAlpha;
25927  if (keyAxis->orientation() == Qt::Horizontal)
25928  {
25929  const int lineCount = valueSize;
25930  const int rowCount = keySize;
25931  for (int line=0; line<lineCount; ++line)
25932  {
25933  QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
25934  if (rawAlpha)
25935  mGradient.colorize(rawData+line*rowCount, rawAlpha+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
25936  else
25937  mGradient.colorize(rawData+line*rowCount, mDataRange, pixels, rowCount, 1, mDataScaleType==QCPAxis::stLogarithmic);
25938  }
25939  } else // keyAxis->orientation() == Qt::Vertical
25940  {
25941  const int lineCount = keySize;
25942  const int rowCount = valueSize;
25943  for (int line=0; line<lineCount; ++line)
25944  {
25945  QRgb* pixels = reinterpret_cast<QRgb*>(localMapImage->scanLine(lineCount-1-line)); // invert scanline index because QImage counts scanlines from top, but our vertical index counts from bottom (mathematical coordinate system)
25946  if (rawAlpha)
25947  mGradient.colorize(rawData+line, rawAlpha+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
25948  else
25949  mGradient.colorize(rawData+line, mDataRange, pixels, rowCount, lineCount, mDataScaleType==QCPAxis::stLogarithmic);
25950  }
25951  }
25952 
25953  if (keyOversamplingFactor > 1 || valueOversamplingFactor > 1)
25954  {
25955  if (keyAxis->orientation() == Qt::Horizontal)
25956  mMapImage = mUndersampledMapImage.scaled(keySize*keyOversamplingFactor, valueSize*valueOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
25957  else
25958  mMapImage = mUndersampledMapImage.scaled(valueSize*valueOversamplingFactor, keySize*keyOversamplingFactor, Qt::IgnoreAspectRatio, Qt::FastTransformation);
25959  }
25960  }
25961  mMapData->mDataModified = false;
25962  mMapImageInvalidated = false;
25963 }
25964 
25965 /* inherits documentation from base class */
25967 {
25968  if (mMapData->isEmpty()) return;
25969  if (!mKeyAxis || !mValueAxis) return;
25971 
25973  updateMapImage();
25974 
25975  // use buffer if painting vectorized (PDF):
25976  const bool useBuffer = painter->modes().testFlag(QCPPainter::pmVectorized);
25977  QCPPainter *localPainter = painter; // will be redirected to paint on mapBuffer if painting vectorized
25978  QRectF mapBufferTarget; // the rect in absolute widget coordinates where the visible map portion/buffer will end up in
25979  QPixmap mapBuffer;
25980  if (useBuffer)
25981  {
25982  const double mapBufferPixelRatio = 3; // factor by which DPI is increased in embedded bitmaps
25983  mapBufferTarget = painter->clipRegion().boundingRect();
25984  mapBuffer = QPixmap((mapBufferTarget.size()*mapBufferPixelRatio).toSize());
25985  mapBuffer.fill(Qt::transparent);
25986  localPainter = new QCPPainter(&mapBuffer);
25987  localPainter->scale(mapBufferPixelRatio, mapBufferPixelRatio);
25988  localPainter->translate(-mapBufferTarget.topLeft());
25989  }
25990 
25991  QRectF imageRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
25993  // extend imageRect to contain outer halves/quarters of bordering/cornering pixels (cells are centered on map range boundary):
25994  double halfCellWidth = 0; // in pixels
25995  double halfCellHeight = 0; // in pixels
25996  if (keyAxis()->orientation() == Qt::Horizontal)
25997  {
25998  if (mMapData->keySize() > 1)
25999  halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->keySize()-1);
26000  if (mMapData->valueSize() > 1)
26001  halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->valueSize()-1);
26002  } else // keyAxis orientation is Qt::Vertical
26003  {
26004  if (mMapData->keySize() > 1)
26005  halfCellHeight = 0.5*imageRect.height()/(double)(mMapData->keySize()-1);
26006  if (mMapData->valueSize() > 1)
26007  halfCellWidth = 0.5*imageRect.width()/(double)(mMapData->valueSize()-1);
26008  }
26009  imageRect.adjust(-halfCellWidth, -halfCellHeight, halfCellWidth, halfCellHeight);
26010  const bool mirrorX = (keyAxis()->orientation() == Qt::Horizontal ? keyAxis() : valueAxis())->rangeReversed();
26011  const bool mirrorY = (valueAxis()->orientation() == Qt::Vertical ? valueAxis() : keyAxis())->rangeReversed();
26012  const bool smoothBackup = localPainter->renderHints().testFlag(QPainter::SmoothPixmapTransform);
26013  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, mInterpolate);
26014  QRegion clipBackup;
26015  if (mTightBoundary)
26016  {
26017  clipBackup = localPainter->clipRegion();
26018  QRectF tightClipRect = QRectF(coordsToPixels(mMapData->keyRange().lower, mMapData->valueRange().lower),
26020  localPainter->setClipRect(tightClipRect, Qt::IntersectClip);
26021  }
26022  localPainter->drawImage(imageRect, mMapImage.mirrored(mirrorX, mirrorY));
26023  if (mTightBoundary)
26024  localPainter->setClipRegion(clipBackup);
26025  localPainter->setRenderHint(QPainter::SmoothPixmapTransform, smoothBackup);
26026 
26027  if (useBuffer) // localPainter painted to mapBuffer, so now draw buffer with original painter
26028  {
26029  delete localPainter;
26030  painter->drawPixmap(mapBufferTarget.toRect(), mapBuffer);
26031  }
26032 }
26033 
26034 /* inherits documentation from base class */
26035 void QCPColorMap::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
26036 {
26038  // draw map thumbnail:
26039  if (!mLegendIcon.isNull())
26040  {
26041  QPixmap scaledIcon = mLegendIcon.scaled(rect.size().toSize(), Qt::KeepAspectRatio, Qt::FastTransformation);
26042  QRectF iconRect = QRectF(0, 0, scaledIcon.width(), scaledIcon.height());
26043  iconRect.moveCenter(rect.center());
26044  painter->drawPixmap(iconRect.topLeft(), scaledIcon);
26045  }
26046  /*
26047  // draw frame:
26048  painter->setBrush(Qt::NoBrush);
26049  painter->setPen(Qt::black);
26050  painter->drawRect(rect.adjusted(1, 1, 0, 0));
26051  */
26052 }
26053 /* end of 'src/plottables/plottable-colormap.cpp' */
26054 
26055 
26056 /* including file 'src/plottables/plottable-financial.cpp', size 42610 */
26057 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
26058 
26062 
26080 /* start documentation of inline functions */
26081 
26131 /* end documentation of inline functions */
26132 
26137  key(0),
26138  open(0),
26139  high(0),
26140  low(0),
26141  close(0)
26142 {
26143 }
26144 
26148 QCPFinancialData::QCPFinancialData(double key, double open, double high, double low, double close) :
26149  key(key),
26150  open(open),
26151  high(high),
26152  low(low),
26153  close(close)
26154 {
26155 }
26156 
26157 
26161 
26211 /* start of documentation of inline functions */
26212 
26220 /* end of documentation of inline functions */
26221 
26233  QCPAbstractPlottable1D<QCPFinancialData>(keyAxis, valueAxis),
26234  mChartStyle(csCandlestick),
26235  mWidth(0.5),
26236  mWidthType(wtPlotCoords),
26237  mTwoColored(true),
26238  mBrushPositive(QBrush(QColor(50, 160, 0))),
26239  mBrushNegative(QBrush(QColor(180, 0, 15))),
26240  mPenPositive(QPen(QColor(40, 150, 0))),
26241  mPenNegative(QPen(QColor(170, 5, 5)))
26242 {
26243  mSelectionDecorator->setBrush(QBrush(QColor(160, 160, 255)));
26244 }
26245 
26247 {
26248 }
26249 
26265 void QCPFinancial::setData(QSharedPointer<QCPFinancialDataContainer> data)
26266 {
26267  mDataContainer = data;
26268 }
26269 
26281 void QCPFinancial::setData(const QVector<double> &keys, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close, bool alreadySorted)
26282 {
26283  mDataContainer->clear();
26284  addData(keys, open, high, low, close, alreadySorted);
26285 }
26286 
26291 {
26292  mChartStyle = style;
26293 }
26294 
26301 {
26302  mWidth = width;
26303 }
26304 
26314 {
26316 }
26317 
26328 {
26330 }
26331 
26342 {
26344 }
26345 
26356 {
26358 }
26359 
26370 {
26371  mPenPositive = pen;
26372 }
26373 
26384 {
26385  mPenNegative = pen;
26386 }
26387 
26402 void QCPFinancial::addData(const QVector<double> &keys, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close, bool alreadySorted)
26403 {
26404  if (keys.size() != open.size() || open.size() != high.size() || high.size() != low.size() || low.size() != close.size() || close.size() != keys.size())
26405  qDebug() << Q_FUNC_INFO << "keys, open, high, low, close have different sizes:" << keys.size() << open.size() << high.size() << low.size() << close.size();
26406  const int n = qMin(keys.size(), qMin(open.size(), qMin(high.size(), qMin(low.size(), close.size()))));
26407  QVector<QCPFinancialData> tempData(n);
26408  QVector<QCPFinancialData>::iterator it = tempData.begin();
26409  const QVector<QCPFinancialData>::iterator itEnd = tempData.end();
26410  int i = 0;
26411  while (it != itEnd)
26412  {
26413  it->key = keys[i];
26414  it->open = open[i];
26415  it->high = high[i];
26416  it->low = low[i];
26417  it->close = close[i];
26418  ++it;
26419  ++i;
26420  }
26421  mDataContainer->add(tempData, alreadySorted); // don't modify tempData beyond this to prevent copy on write
26422 }
26423 
26434 void QCPFinancial::addData(double key, double open, double high, double low, double close)
26435 {
26436  mDataContainer->add(QCPFinancialData(key, open, high, low, close));
26437 }
26438 
26442 QCPDataSelection QCPFinancial::selectTestRect(const QRectF &rect, bool onlySelectable) const
26443 {
26444  QCPDataSelection result;
26445  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
26446  return result;
26447  if (!mKeyAxis || !mValueAxis)
26448  return result;
26449 
26450  QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
26451  getVisibleDataBounds(visibleBegin, visibleEnd);
26452 
26453  for (QCPFinancialDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
26454  {
26455  if (rect.intersects(selectionHitBox(it)))
26456  result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
26457  }
26458  result.simplify();
26459  return result;
26460 }
26461 
26462 /* inherits documentation from base class */
26463 double QCPFinancial::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
26464 {
26465  Q_UNUSED(details)
26466  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
26467  return -1;
26468  if (!mKeyAxis || !mValueAxis)
26469  return -1;
26470 
26471  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
26472  {
26473  // get visible data range:
26474  QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
26475  QCPFinancialDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
26476  getVisibleDataBounds(visibleBegin, visibleEnd);
26477  // perform select test according to configured style:
26478  double result = -1;
26479  switch (mChartStyle)
26480  {
26481  case QCPFinancial::csOhlc:
26482  result = ohlcSelectTest(pos, visibleBegin, visibleEnd, closestDataPoint); break;
26484  result = candlestickSelectTest(pos, visibleBegin, visibleEnd, closestDataPoint); break;
26485  }
26486  if (details)
26487  {
26488  int pointIndex = closestDataPoint-mDataContainer->constBegin();
26489  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
26490  }
26491  return result;
26492  }
26493 
26494  return -1;
26495 }
26496 
26497 /* inherits documentation from base class */
26498 QCPRange QCPFinancial::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
26499 {
26500  QCPRange range = mDataContainer->keyRange(foundRange, inSignDomain);
26501  // determine exact range by including width of bars/flags:
26502  if (foundRange)
26503  {
26504  if (inSignDomain != QCP::sdPositive || range.lower-mWidth*0.5 > 0)
26505  range.lower -= mWidth*0.5;
26506  if (inSignDomain != QCP::sdNegative || range.upper+mWidth*0.5 < 0)
26507  range.upper += mWidth*0.5;
26508  }
26509  return range;
26510 }
26511 
26512 /* inherits documentation from base class */
26513 QCPRange QCPFinancial::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
26514 {
26515  return mDataContainer->valueRange(foundRange, inSignDomain, inKeyRange);
26516 }
26517 
26532 QCPFinancialDataContainer QCPFinancial::timeSeriesToOhlc(const QVector<double> &time, const QVector<double> &value, double timeBinSize, double timeBinOffset)
26533 {
26535  int count = qMin(time.size(), value.size());
26536  if (count == 0)
26537  return QCPFinancialDataContainer();
26538 
26539  QCPFinancialData currentBinData(0, value.first(), value.first(), value.first(), value.first());
26540  int currentBinIndex = qFloor((time.first()-timeBinOffset)/timeBinSize+0.5);
26541  for (int i=0; i<count; ++i)
26542  {
26543  int index = qFloor((time.at(i)-timeBinOffset)/timeBinSize+0.5);
26544  if (currentBinIndex == index) // data point still in current bin, extend high/low:
26545  {
26546  if (value.at(i) < currentBinData.low) currentBinData.low = value.at(i);
26547  if (value.at(i) > currentBinData.high) currentBinData.high = value.at(i);
26548  if (i == count-1) // last data point is in current bin, finalize bin:
26549  {
26550  currentBinData.close = value.at(i);
26551  currentBinData.key = timeBinOffset+(index)*timeBinSize;
26552  data.add(currentBinData);
26553  }
26554  } else // data point not anymore in current bin, set close of old and open of new bin, and add old to map:
26555  {
26556  // finalize current bin:
26557  currentBinData.close = value.at(i-1);
26558  currentBinData.key = timeBinOffset+(index-1)*timeBinSize;
26559  data.add(currentBinData);
26560  // start next bin:
26561  currentBinIndex = index;
26562  currentBinData.open = value.at(i);
26563  currentBinData.high = value.at(i);
26564  currentBinData.low = value.at(i);
26565  }
26566  }
26567 
26568  return data;
26569 }
26570 
26571 /* inherits documentation from base class */
26573 {
26574  // get visible data range:
26575  QCPFinancialDataContainer::const_iterator visibleBegin, visibleEnd;
26576  getVisibleDataBounds(visibleBegin, visibleEnd);
26577 
26578  // loop over and draw segments of unselected/selected data:
26579  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
26580  getDataSegments(selectedSegments, unselectedSegments);
26581  allSegments << unselectedSegments << selectedSegments;
26582  for (int i=0; i<allSegments.size(); ++i)
26583  {
26584  bool isSelectedSegment = i >= unselectedSegments.size();
26585  QCPFinancialDataContainer::const_iterator begin = visibleBegin;
26587  mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
26588  if (begin == end)
26589  continue;
26590 
26591  // draw data segment according to configured style:
26592  switch (mChartStyle)
26593  {
26594  case QCPFinancial::csOhlc:
26595  drawOhlcPlot(painter, begin, end, isSelectedSegment); break;
26597  drawCandlestickPlot(painter, begin, end, isSelectedSegment); break;
26598  }
26599  }
26600 
26601  // draw other selection decoration that isn't just line/scatter pens and brushes:
26602  if (mSelectionDecorator)
26604 }
26605 
26606 /* inherits documentation from base class */
26607 void QCPFinancial::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
26608 {
26609  painter->setAntialiasing(false); // legend icon especially of csCandlestick looks better without antialiasing
26610  if (mChartStyle == csOhlc)
26611  {
26612  if (mTwoColored)
26613  {
26614  // draw upper left half icon with positive color:
26615  painter->setBrush(mBrushPositive);
26616  painter->setPen(mPenPositive);
26617  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
26618  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26619  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
26620  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
26621  // draw bottom right half icon with negative color:
26622  painter->setBrush(mBrushNegative);
26623  painter->setPen(mPenNegative);
26624  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
26625  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26626  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
26627  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
26628  } else
26629  {
26630  painter->setBrush(mBrush);
26631  painter->setPen(mPen);
26632  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26633  painter->drawLine(QLineF(rect.width()*0.2, rect.height()*0.3, rect.width()*0.2, rect.height()*0.5).translated(rect.topLeft()));
26634  painter->drawLine(QLineF(rect.width()*0.8, rect.height()*0.5, rect.width()*0.8, rect.height()*0.7).translated(rect.topLeft()));
26635  }
26636  } else if (mChartStyle == csCandlestick)
26637  {
26638  if (mTwoColored)
26639  {
26640  // draw upper left half icon with positive color:
26641  painter->setBrush(mBrushPositive);
26642  painter->setPen(mPenPositive);
26643  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.topLeft().toPoint()));
26644  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
26645  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26646  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
26647  // draw bottom right half icon with negative color:
26648  painter->setBrush(mBrushNegative);
26649  painter->setPen(mPenNegative);
26650  painter->setClipRegion(QRegion(QPolygon() << rect.bottomLeft().toPoint() << rect.topRight().toPoint() << rect.bottomRight().toPoint()));
26651  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
26652  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26653  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
26654  } else
26655  {
26656  painter->setBrush(mBrush);
26657  painter->setPen(mPen);
26658  painter->drawLine(QLineF(0, rect.height()*0.5, rect.width()*0.25, rect.height()*0.5).translated(rect.topLeft()));
26659  painter->drawLine(QLineF(rect.width()*0.75, rect.height()*0.5, rect.width(), rect.height()*0.5).translated(rect.topLeft()));
26660  painter->drawRect(QRectF(rect.width()*0.25, rect.height()*0.25, rect.width()*0.5, rect.height()*0.5).translated(rect.topLeft()));
26661  }
26662  }
26663 }
26664 
26672 {
26673  QCPAxis *keyAxis = mKeyAxis.data();
26674  QCPAxis *valueAxis = mValueAxis.data();
26675  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
26676 
26677  if (keyAxis->orientation() == Qt::Horizontal)
26678  {
26679  for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
26680  {
26681  if (isSelected && mSelectionDecorator)
26682  mSelectionDecorator->applyPen(painter);
26683  else if (mTwoColored)
26684  painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
26685  else
26686  painter->setPen(mPen);
26687  double keyPixel = keyAxis->coordToPixel(it->key);
26688  double openPixel = valueAxis->coordToPixel(it->open);
26689  double closePixel = valueAxis->coordToPixel(it->close);
26690  // draw backbone:
26691  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->high)), QPointF(keyPixel, valueAxis->coordToPixel(it->low)));
26692  // draw open:
26693  double pixelWidth = getPixelWidth(it->key, keyPixel); // sign of this makes sure open/close are on correct sides
26694  painter->drawLine(QPointF(keyPixel-pixelWidth, openPixel), QPointF(keyPixel, openPixel));
26695  // draw close:
26696  painter->drawLine(QPointF(keyPixel, closePixel), QPointF(keyPixel+pixelWidth, closePixel));
26697  }
26698  } else
26699  {
26700  for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
26701  {
26702  if (isSelected && mSelectionDecorator)
26703  mSelectionDecorator->applyPen(painter);
26704  else if (mTwoColored)
26705  painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
26706  else
26707  painter->setPen(mPen);
26708  double keyPixel = keyAxis->coordToPixel(it->key);
26709  double openPixel = valueAxis->coordToPixel(it->open);
26710  double closePixel = valueAxis->coordToPixel(it->close);
26711  // draw backbone:
26712  painter->drawLine(QPointF(valueAxis->coordToPixel(it->high), keyPixel), QPointF(valueAxis->coordToPixel(it->low), keyPixel));
26713  // draw open:
26714  double pixelWidth = getPixelWidth(it->key, keyPixel); // sign of this makes sure open/close are on correct sides
26715  painter->drawLine(QPointF(openPixel, keyPixel-pixelWidth), QPointF(openPixel, keyPixel));
26716  // draw close:
26717  painter->drawLine(QPointF(closePixel, keyPixel), QPointF(closePixel, keyPixel+pixelWidth));
26718  }
26719  }
26720 }
26721 
26729 {
26730  QCPAxis *keyAxis = mKeyAxis.data();
26731  QCPAxis *valueAxis = mValueAxis.data();
26732  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
26733 
26734  if (keyAxis->orientation() == Qt::Horizontal)
26735  {
26736  for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
26737  {
26738  if (isSelected && mSelectionDecorator)
26739  {
26740  mSelectionDecorator->applyPen(painter);
26741  mSelectionDecorator->applyBrush(painter);
26742  } else if (mTwoColored)
26743  {
26744  painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
26745  painter->setBrush(it->close >= it->open ? mBrushPositive : mBrushNegative);
26746  } else
26747  {
26748  painter->setPen(mPen);
26749  painter->setBrush(mBrush);
26750  }
26751  double keyPixel = keyAxis->coordToPixel(it->key);
26752  double openPixel = valueAxis->coordToPixel(it->open);
26753  double closePixel = valueAxis->coordToPixel(it->close);
26754  // draw high:
26755  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->high)), QPointF(keyPixel, valueAxis->coordToPixel(qMax(it->open, it->close))));
26756  // draw low:
26757  painter->drawLine(QPointF(keyPixel, valueAxis->coordToPixel(it->low)), QPointF(keyPixel, valueAxis->coordToPixel(qMin(it->open, it->close))));
26758  // draw open-close box:
26759  double pixelWidth = getPixelWidth(it->key, keyPixel);
26760  painter->drawRect(QRectF(QPointF(keyPixel-pixelWidth, closePixel), QPointF(keyPixel+pixelWidth, openPixel)));
26761  }
26762  } else // keyAxis->orientation() == Qt::Vertical
26763  {
26764  for (QCPFinancialDataContainer::const_iterator it = begin; it != end; ++it)
26765  {
26766  if (isSelected && mSelectionDecorator)
26767  {
26768  mSelectionDecorator->applyPen(painter);
26769  mSelectionDecorator->applyBrush(painter);
26770  } else if (mTwoColored)
26771  {
26772  painter->setPen(it->close >= it->open ? mPenPositive : mPenNegative);
26773  painter->setBrush(it->close >= it->open ? mBrushPositive : mBrushNegative);
26774  } else
26775  {
26776  painter->setPen(mPen);
26777  painter->setBrush(mBrush);
26778  }
26779  double keyPixel = keyAxis->coordToPixel(it->key);
26780  double openPixel = valueAxis->coordToPixel(it->open);
26781  double closePixel = valueAxis->coordToPixel(it->close);
26782  // draw high:
26783  painter->drawLine(QPointF(valueAxis->coordToPixel(it->high), keyPixel), QPointF(valueAxis->coordToPixel(qMax(it->open, it->close)), keyPixel));
26784  // draw low:
26785  painter->drawLine(QPointF(valueAxis->coordToPixel(it->low), keyPixel), QPointF(valueAxis->coordToPixel(qMin(it->open, it->close)), keyPixel));
26786  // draw open-close box:
26787  double pixelWidth = getPixelWidth(it->key, keyPixel);
26788  painter->drawRect(QRectF(QPointF(closePixel, keyPixel-pixelWidth), QPointF(openPixel, keyPixel+pixelWidth)));
26789  }
26790  }
26791 }
26792 
26805 double QCPFinancial::getPixelWidth(double key, double keyPixel) const
26806 {
26807  double result = 0;
26808  switch (mWidthType)
26809  {
26810  case wtAbsolute:
26811  {
26812  if (mKeyAxis)
26813  result = mWidth*0.5*mKeyAxis.data()->pixelOrientation();
26814  break;
26815  }
26816  case wtAxisRectRatio:
26817  {
26818  if (mKeyAxis && mKeyAxis.data()->axisRect())
26819  {
26820  if (mKeyAxis.data()->orientation() == Qt::Horizontal)
26821  result = mKeyAxis.data()->axisRect()->width()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
26822  else
26823  result = mKeyAxis.data()->axisRect()->height()*mWidth*0.5*mKeyAxis.data()->pixelOrientation();
26824  } else
26825  qDebug() << Q_FUNC_INFO << "No key axis or axis rect defined";
26826  break;
26827  }
26828  case wtPlotCoords:
26829  {
26830  if (mKeyAxis)
26831  result = mKeyAxis.data()->coordToPixel(key+mWidth*0.5)-keyPixel;
26832  else
26833  qDebug() << Q_FUNC_INFO << "No key axis defined";
26834  break;
26835  }
26836  }
26837  return result;
26838 }
26839 
26849 {
26850  closestDataPoint = mDataContainer->constEnd();
26851  QCPAxis *keyAxis = mKeyAxis.data();
26852  QCPAxis *valueAxis = mValueAxis.data();
26853  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
26854 
26855  double minDistSqr = std::numeric_limits<double>::max();
26856  if (keyAxis->orientation() == Qt::Horizontal)
26857  {
26858  for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
26859  {
26860  double keyPixel = keyAxis->coordToPixel(it->key);
26861  // calculate distance to backbone:
26862  double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->high)), QCPVector2D(keyPixel, valueAxis->coordToPixel(it->low)));
26863  if (currentDistSqr < minDistSqr)
26864  {
26865  minDistSqr = currentDistSqr;
26866  closestDataPoint = it;
26867  }
26868  }
26869  } else // keyAxis->orientation() == Qt::Vertical
26870  {
26871  for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
26872  {
26873  double keyPixel = keyAxis->coordToPixel(it->key);
26874  // calculate distance to backbone:
26875  double currentDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->high), keyPixel), QCPVector2D(valueAxis->coordToPixel(it->low), keyPixel));
26876  if (currentDistSqr < minDistSqr)
26877  {
26878  minDistSqr = currentDistSqr;
26879  closestDataPoint = it;
26880  }
26881  }
26882  }
26883  return qSqrt(minDistSqr);
26884 }
26885 
26896 {
26897  closestDataPoint = mDataContainer->constEnd();
26898  QCPAxis *keyAxis = mKeyAxis.data();
26899  QCPAxis *valueAxis = mValueAxis.data();
26900  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return -1; }
26901 
26902  double minDistSqr = std::numeric_limits<double>::max();
26903  if (keyAxis->orientation() == Qt::Horizontal)
26904  {
26905  for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
26906  {
26907  double currentDistSqr;
26908  // determine whether pos is in open-close-box:
26909  QCPRange boxKeyRange(it->key-mWidth*0.5, it->key+mWidth*0.5);
26910  QCPRange boxValueRange(it->close, it->open);
26911  double posKey, posValue;
26912  pixelsToCoords(pos, posKey, posValue);
26913  if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
26914  {
26915  currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
26916  } else
26917  {
26918  // calculate distance to high/low lines:
26919  double keyPixel = keyAxis->coordToPixel(it->key);
26920  double highLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->high)), QCPVector2D(keyPixel, valueAxis->coordToPixel(qMax(it->open, it->close))));
26921  double lowLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(keyPixel, valueAxis->coordToPixel(it->low)), QCPVector2D(keyPixel, valueAxis->coordToPixel(qMin(it->open, it->close))));
26922  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
26923  }
26924  if (currentDistSqr < minDistSqr)
26925  {
26926  minDistSqr = currentDistSqr;
26927  closestDataPoint = it;
26928  }
26929  }
26930  } else // keyAxis->orientation() == Qt::Vertical
26931  {
26932  for (QCPFinancialDataContainer::const_iterator it=begin; it!=end; ++it)
26933  {
26934  double currentDistSqr;
26935  // determine whether pos is in open-close-box:
26936  QCPRange boxKeyRange(it->key-mWidth*0.5, it->key+mWidth*0.5);
26937  QCPRange boxValueRange(it->close, it->open);
26938  double posKey, posValue;
26939  pixelsToCoords(pos, posKey, posValue);
26940  if (boxKeyRange.contains(posKey) && boxValueRange.contains(posValue)) // is in open-close-box
26941  {
26942  currentDistSqr = mParentPlot->selectionTolerance()*0.99 * mParentPlot->selectionTolerance()*0.99;
26943  } else
26944  {
26945  // calculate distance to high/low lines:
26946  double keyPixel = keyAxis->coordToPixel(it->key);
26947  double highLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->high), keyPixel), QCPVector2D(valueAxis->coordToPixel(qMax(it->open, it->close)), keyPixel));
26948  double lowLineDistSqr = QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(valueAxis->coordToPixel(it->low), keyPixel), QCPVector2D(valueAxis->coordToPixel(qMin(it->open, it->close)), keyPixel));
26949  currentDistSqr = qMin(highLineDistSqr, lowLineDistSqr);
26950  }
26951  if (currentDistSqr < minDistSqr)
26952  {
26953  minDistSqr = currentDistSqr;
26954  closestDataPoint = it;
26955  }
26956  }
26957  }
26958  return qSqrt(minDistSqr);
26959 }
26960 
26976 {
26977  if (!mKeyAxis)
26978  {
26979  qDebug() << Q_FUNC_INFO << "invalid key axis";
26980  begin = mDataContainer->constEnd();
26981  end = mDataContainer->constEnd();
26982  return;
26983  }
26984  begin = mDataContainer->findBegin(mKeyAxis.data()->range().lower-mWidth*0.5); // subtract half width of ohlc/candlestick to include partially visible data points
26985  end = mDataContainer->findEnd(mKeyAxis.data()->range().upper+mWidth*0.5); // add half width of ohlc/candlestick to include partially visible data points
26986 }
26987 
26994 {
26995  QCPAxis *keyAxis = mKeyAxis.data();
26996  QCPAxis *valueAxis = mValueAxis.data();
26997  if (!keyAxis || !valueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return QRectF(); }
26998 
26999  double keyPixel = keyAxis->coordToPixel(it->key);
27000  double highPixel = valueAxis->coordToPixel(it->high);
27001  double lowPixel = valueAxis->coordToPixel(it->low);
27002  double keyWidthPixels = keyPixel-keyAxis->coordToPixel(it->key-mWidth*0.5);
27003  if (keyAxis->orientation() == Qt::Horizontal)
27004  return QRectF(keyPixel-keyWidthPixels, highPixel, keyWidthPixels*2, lowPixel-highPixel).normalized();
27005  else
27006  return QRectF(highPixel, keyPixel-keyWidthPixels, lowPixel-highPixel, keyWidthPixels*2).normalized();
27007 }
27008 /* end of 'src/plottables/plottable-financial.cpp' */
27009 
27010 
27011 /* including file 'src/plottables/plottable-errorbar.cpp', size 37355 */
27012 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
27013 
27017 
27037  errorMinus(0),
27038  errorPlus(0)
27039 {
27040 }
27041 
27046  errorMinus(error),
27047  errorPlus(error)
27048 {
27049 }
27050 
27056  errorMinus(errorMinus),
27057  errorPlus(errorPlus)
27058 {
27059 }
27060 
27061 
27065 
27095 /* start of documentation of inline functions */
27096 
27104 /* end of documentation of inline functions */
27105 
27120  QCPAbstractPlottable(keyAxis, valueAxis),
27121  mDataContainer(new QVector<QCPErrorBarsData>),
27122  mErrorType(etValueError),
27123  mWhiskerWidth(9),
27124  mSymbolGap(10)
27125 {
27126  setPen(QPen(Qt::black, 0));
27127  setBrush(Qt::NoBrush);
27128 }
27129 
27131 {
27132 }
27133 
27152 void QCPErrorBars::setData(QSharedPointer<QCPErrorBarsDataContainer> data)
27153 {
27154  mDataContainer = data;
27155 }
27156 
27166 void QCPErrorBars::setData(const QVector<double> &error)
27167 {
27168  mDataContainer->clear();
27169  addData(error);
27170 }
27171 
27182 void QCPErrorBars::setData(const QVector<double> &errorMinus, const QVector<double> &errorPlus)
27183 {
27184  mDataContainer->clear();
27185  addData(errorMinus, errorPlus);
27186 }
27187 
27203 {
27204  if (plottable && qobject_cast<QCPErrorBars*>(plottable))
27205  {
27206  mDataPlottable = 0;
27207  qDebug() << Q_FUNC_INFO << "can't set another QCPErrorBars instance as data plottable";
27208  return;
27209  }
27210  if (plottable && !plottable->interface1D())
27211  {
27212  mDataPlottable = 0;
27213  qDebug() << Q_FUNC_INFO << "passed plottable doesn't implement 1d interface, can't associate with QCPErrorBars";
27214  return;
27215  }
27216 
27217  mDataPlottable = plottable;
27218 }
27219 
27225 {
27226  mErrorType = type;
27227 }
27228 
27234 {
27235  mWhiskerWidth = pixels;
27236 }
27237 
27243 void QCPErrorBars::setSymbolGap(double pixels)
27244 {
27245  mSymbolGap = pixels;
27246 }
27247 
27257 void QCPErrorBars::addData(const QVector<double> &error)
27258 {
27259  addData(error, error);
27260 }
27261 
27272 void QCPErrorBars::addData(const QVector<double> &errorMinus, const QVector<double> &errorPlus)
27273 {
27274  if (errorMinus.size() != errorPlus.size())
27275  qDebug() << Q_FUNC_INFO << "minus and plus error vectors have different sizes:" << errorMinus.size() << errorPlus.size();
27276  const int n = qMin(errorMinus.size(), errorPlus.size());
27277  mDataContainer->reserve(n);
27278  for (int i=0; i<n; ++i)
27279  mDataContainer->append(QCPErrorBarsData(errorMinus.at(i), errorPlus.at(i)));
27280 }
27281 
27291 void QCPErrorBars::addData(double error)
27292 {
27293  mDataContainer->append(QCPErrorBarsData(error));
27294 }
27295 
27306 void QCPErrorBars::addData(double errorMinus, double errorPlus)
27307 {
27308  mDataContainer->append(QCPErrorBarsData(errorMinus, errorPlus));
27309 }
27310 
27311 /* inherits documentation from base class */
27313 {
27314  return mDataContainer->size();
27315 }
27316 
27317 /* inherits documentation from base class */
27318 double QCPErrorBars::dataMainKey(int index) const
27319 {
27320  if (mDataPlottable)
27321  return mDataPlottable->interface1D()->dataMainKey(index);
27322  else
27323  qDebug() << Q_FUNC_INFO << "no data plottable set";
27324  return 0;
27325 }
27326 
27327 /* inherits documentation from base class */
27328 double QCPErrorBars::dataSortKey(int index) const
27329 {
27330  if (mDataPlottable)
27331  return mDataPlottable->interface1D()->dataSortKey(index);
27332  else
27333  qDebug() << Q_FUNC_INFO << "no data plottable set";
27334  return 0;
27335 }
27336 
27337 /* inherits documentation from base class */
27338 double QCPErrorBars::dataMainValue(int index) const
27339 {
27340  if (mDataPlottable)
27341  return mDataPlottable->interface1D()->dataMainValue(index);
27342  else
27343  qDebug() << Q_FUNC_INFO << "no data plottable set";
27344  return 0;
27345 }
27346 
27347 /* inherits documentation from base class */
27349 {
27350  if (mDataPlottable)
27351  {
27352  const double value = mDataPlottable->interface1D()->dataMainValue(index);
27353  if (index >= 0 && index < mDataContainer->size() && mErrorType == etValueError)
27354  return QCPRange(value-mDataContainer->at(index).errorMinus, value+mDataContainer->at(index).errorPlus);
27355  else
27356  return QCPRange(value, value);
27357  } else
27358  {
27359  qDebug() << Q_FUNC_INFO << "no data plottable set";
27360  return QCPRange();
27361  }
27362 }
27363 
27364 /* inherits documentation from base class */
27365 QPointF QCPErrorBars::dataPixelPosition(int index) const
27366 {
27367  if (mDataPlottable)
27368  return mDataPlottable->interface1D()->dataPixelPosition(index);
27369  else
27370  qDebug() << Q_FUNC_INFO << "no data plottable set";
27371  return QPointF();
27372 }
27373 
27374 /* inherits documentation from base class */
27376 {
27377  if (mDataPlottable)
27378  {
27379  return mDataPlottable->interface1D()->sortKeyIsMainKey();
27380  } else
27381  {
27382  qDebug() << Q_FUNC_INFO << "no data plottable set";
27383  return true;
27384  }
27385 }
27386 
27390 QCPDataSelection QCPErrorBars::selectTestRect(const QRectF &rect, bool onlySelectable) const
27391 {
27392  QCPDataSelection result;
27393  if (!mDataPlottable)
27394  return result;
27395  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
27396  return result;
27397  if (!mKeyAxis || !mValueAxis)
27398  return result;
27399 
27400  QCPErrorBarsDataContainer::const_iterator visibleBegin, visibleEnd;
27401  getVisibleDataBounds(visibleBegin, visibleEnd, QCPDataRange(0, dataCount()));
27402 
27403  QVector<QLineF> backbones, whiskers;
27404  for (QCPErrorBarsDataContainer::const_iterator it=visibleBegin; it!=visibleEnd; ++it)
27405  {
27406  backbones.clear();
27407  whiskers.clear();
27408  getErrorBarLines(it, backbones, whiskers);
27409  for (int i=0; i<backbones.size(); ++i)
27410  {
27411  if (rectIntersectsLine(rect, backbones.at(i)))
27412  {
27413  result.addDataRange(QCPDataRange(it-mDataContainer->constBegin(), it-mDataContainer->constBegin()+1), false);
27414  break;
27415  }
27416  }
27417  }
27418  result.simplify();
27419  return result;
27420 }
27421 
27422 /* inherits documentation from base class */
27423 int QCPErrorBars::findBegin(double sortKey, bool expandedRange) const
27424 {
27425  if (mDataPlottable)
27426  {
27427  if (mDataContainer->isEmpty())
27428  return 0;
27429  int beginIndex = mDataPlottable->interface1D()->findBegin(sortKey, expandedRange);
27430  if (beginIndex >= mDataContainer->size())
27431  beginIndex = mDataContainer->size()-1;
27432  return beginIndex;
27433  } else
27434  qDebug() << Q_FUNC_INFO << "no data plottable set";
27435  return 0;
27436 }
27437 
27438 /* inherits documentation from base class */
27439 int QCPErrorBars::findEnd(double sortKey, bool expandedRange) const
27440 {
27441  if (mDataPlottable)
27442  {
27443  if (mDataContainer->isEmpty())
27444  return 0;
27445  int endIndex = mDataPlottable->interface1D()->findEnd(sortKey, expandedRange);
27446  if (endIndex > mDataContainer->size())
27447  endIndex = mDataContainer->size();
27448  return endIndex;
27449  } else
27450  qDebug() << Q_FUNC_INFO << "no data plottable set";
27451  return 0;
27452 }
27453 
27454 /* inherits documentation from base class */
27455 double QCPErrorBars::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
27456 {
27457  if (!mDataPlottable) return -1;
27458 
27459  if ((onlySelectable && mSelectable == QCP::stNone) || mDataContainer->isEmpty())
27460  return -1;
27461  if (!mKeyAxis || !mValueAxis)
27462  return -1;
27463 
27464  if (mKeyAxis.data()->axisRect()->rect().contains(pos.toPoint()))
27465  {
27466  QCPErrorBarsDataContainer::const_iterator closestDataPoint = mDataContainer->constEnd();
27467  double result = pointDistance(pos, closestDataPoint);
27468  if (details)
27469  {
27470  int pointIndex = closestDataPoint-mDataContainer->constBegin();
27471  details->setValue(QCPDataSelection(QCPDataRange(pointIndex, pointIndex+1)));
27472  }
27473  return result;
27474  } else
27475  return -1;
27476 }
27477 
27478 /* inherits documentation from base class */
27480 {
27481  if (!mDataPlottable) return;
27482  if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
27483  if (mKeyAxis.data()->range().size() <= 0 || mDataContainer->isEmpty()) return;
27484 
27485  // if the sort key isn't the main key, we must check the visibility for each data point/error bar individually
27486  // (getVisibleDataBounds applies range restriction, but otherwise can only return full data range):
27487  bool checkPointVisibility = !mDataPlottable->interface1D()->sortKeyIsMainKey();
27488 
27489  // check data validity if flag set:
27490 #ifdef QCUSTOMPLOT_CHECK_DATA
27491  QCPErrorBarsDataContainer::const_iterator it;
27492  for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
27493  {
27494  if (QCP::isInvalidData(it->errorMinus, it->errorPlus))
27495  qDebug() << Q_FUNC_INFO << "Data point at index" << it-mDataContainer->constBegin() << "invalid." << "Plottable name:" << name();
27496  }
27497 #endif
27498 
27500  painter->setBrush(Qt::NoBrush);
27501  // loop over and draw segments of unselected/selected data:
27502  QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
27503  getDataSegments(selectedSegments, unselectedSegments);
27504  allSegments << unselectedSegments << selectedSegments;
27505  QVector<QLineF> backbones, whiskers;
27506  for (int i=0; i<allSegments.size(); ++i)
27507  {
27508  QCPErrorBarsDataContainer::const_iterator begin, end;
27509  getVisibleDataBounds(begin, end, allSegments.at(i));
27510  if (begin == end)
27511  continue;
27512 
27513  bool isSelectedSegment = i >= unselectedSegments.size();
27514  if (isSelectedSegment && mSelectionDecorator)
27515  mSelectionDecorator->applyPen(painter);
27516  else
27517  painter->setPen(mPen);
27518  if (painter->pen().capStyle() == Qt::SquareCap)
27519  {
27520  QPen capFixPen(painter->pen());
27521  capFixPen.setCapStyle(Qt::FlatCap);
27522  painter->setPen(capFixPen);
27523  }
27524  backbones.clear();
27525  whiskers.clear();
27526  for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it)
27527  {
27528  if (!checkPointVisibility || errorBarVisible(it-mDataContainer->constBegin()))
27529  getErrorBarLines(it, backbones, whiskers);
27530  }
27531  painter->drawLines(backbones);
27532  painter->drawLines(whiskers);
27533  }
27534 
27535  // draw other selection decoration that isn't just line/scatter pens and brushes:
27536  if (mSelectionDecorator)
27538 }
27539 
27540 /* inherits documentation from base class */
27541 void QCPErrorBars::drawLegendIcon(QCPPainter *painter, const QRectF &rect) const
27542 {
27544  painter->setPen(mPen);
27545  if (mErrorType == etValueError && mValueAxis && mValueAxis->orientation() == Qt::Vertical)
27546  {
27547  painter->drawLine(QLineF(rect.center().x(), rect.top()+2, rect.center().x(), rect.bottom()-1));
27548  painter->drawLine(QLineF(rect.center().x()-4, rect.top()+2, rect.center().x()+4, rect.top()+2));
27549  painter->drawLine(QLineF(rect.center().x()-4, rect.bottom()-1, rect.center().x()+4, rect.bottom()-1));
27550  } else
27551  {
27552  painter->drawLine(QLineF(rect.left()+2, rect.center().y(), rect.right()-2, rect.center().y()));
27553  painter->drawLine(QLineF(rect.left()+2, rect.center().y()-4, rect.left()+2, rect.center().y()+4));
27554  painter->drawLine(QLineF(rect.right()-2, rect.center().y()-4, rect.right()-2, rect.center().y()+4));
27555  }
27556 }
27557 
27558 /* inherits documentation from base class */
27559 QCPRange QCPErrorBars::getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain) const
27560 {
27561  if (!mDataPlottable)
27562  {
27563  foundRange = false;
27564  return QCPRange();
27565  }
27566 
27567  QCPRange range;
27568  bool haveLower = false;
27569  bool haveUpper = false;
27570  QCPErrorBarsDataContainer::const_iterator it;
27571  for (it = mDataContainer->constBegin(); it != mDataContainer->constEnd(); ++it)
27572  {
27573  if (mErrorType == etValueError)
27574  {
27575  // error bar doesn't extend in key dimension (except whisker but we ignore that here), so only use data point center
27576  const double current = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin());
27577  if (qIsNaN(current)) continue;
27578  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27579  {
27580  if (current < range.lower || !haveLower)
27581  {
27582  range.lower = current;
27583  haveLower = true;
27584  }
27585  if (current > range.upper || !haveUpper)
27586  {
27587  range.upper = current;
27588  haveUpper = true;
27589  }
27590  }
27591  } else // mErrorType == etKeyError
27592  {
27593  const double dataKey = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin());
27594  if (qIsNaN(dataKey)) continue;
27595  // plus error:
27596  double current = dataKey + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus);
27597  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27598  {
27599  if (current > range.upper || !haveUpper)
27600  {
27601  range.upper = current;
27602  haveUpper = true;
27603  }
27604  }
27605  // minus error:
27606  current = dataKey - (qIsNaN(it->errorMinus) ? 0 : it->errorMinus);
27607  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27608  {
27609  if (current < range.lower || !haveLower)
27610  {
27611  range.lower = current;
27612  haveLower = true;
27613  }
27614  }
27615  }
27616  }
27617 
27618  if (haveUpper && !haveLower)
27619  {
27620  range.lower = range.upper;
27621  haveLower = true;
27622  } else if (haveLower && !haveUpper)
27623  {
27624  range.upper = range.lower;
27625  haveUpper = true;
27626  }
27627 
27628  foundRange = haveLower && haveUpper;
27629  return range;
27630 }
27631 
27632 /* inherits documentation from base class */
27633 QCPRange QCPErrorBars::getValueRange(bool &foundRange, QCP::SignDomain inSignDomain, const QCPRange &inKeyRange) const
27634 {
27635  if (!mDataPlottable)
27636  {
27637  foundRange = false;
27638  return QCPRange();
27639  }
27640 
27641  QCPRange range;
27642  const bool restrictKeyRange = inKeyRange != QCPRange();
27643  bool haveLower = false;
27644  bool haveUpper = false;
27645  QCPErrorBarsDataContainer::const_iterator itBegin = mDataContainer->constBegin();
27646  QCPErrorBarsDataContainer::const_iterator itEnd = mDataContainer->constEnd();
27647  if (mDataPlottable->interface1D()->sortKeyIsMainKey() && restrictKeyRange)
27648  {
27649  itBegin = mDataContainer->constBegin()+findBegin(inKeyRange.lower);
27650  itEnd = mDataContainer->constBegin()+findEnd(inKeyRange.upper);
27651  }
27652  for (QCPErrorBarsDataContainer::const_iterator it = itBegin; it != itEnd; ++it)
27653  {
27654  if (restrictKeyRange)
27655  {
27656  const double dataKey = mDataPlottable->interface1D()->dataMainKey(it-mDataContainer->constBegin());
27657  if (dataKey < inKeyRange.lower || dataKey > inKeyRange.upper)
27658  continue;
27659  }
27660  if (mErrorType == etValueError)
27661  {
27662  const double dataValue = mDataPlottable->interface1D()->dataMainValue(it-mDataContainer->constBegin());
27663  if (qIsNaN(dataValue)) continue;
27664  // plus error:
27665  double current = dataValue + (qIsNaN(it->errorPlus) ? 0 : it->errorPlus);
27666  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27667  {
27668  if (current > range.upper || !haveUpper)
27669  {
27670  range.upper = current;
27671  haveUpper = true;
27672  }
27673  }
27674  // minus error:
27675  current = dataValue - (qIsNaN(it->errorMinus) ? 0 : it->errorMinus);
27676  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27677  {
27678  if (current < range.lower || !haveLower)
27679  {
27680  range.lower = current;
27681  haveLower = true;
27682  }
27683  }
27684  } else // mErrorType == etKeyError
27685  {
27686  // error bar doesn't extend in value dimension (except whisker but we ignore that here), so only use data point center
27687  const double current = mDataPlottable->interface1D()->dataMainValue(it-mDataContainer->constBegin());
27688  if (qIsNaN(current)) continue;
27689  if (inSignDomain == QCP::sdBoth || (inSignDomain == QCP::sdNegative && current < 0) || (inSignDomain == QCP::sdPositive && current > 0))
27690  {
27691  if (current < range.lower || !haveLower)
27692  {
27693  range.lower = current;
27694  haveLower = true;
27695  }
27696  if (current > range.upper || !haveUpper)
27697  {
27698  range.upper = current;
27699  haveUpper = true;
27700  }
27701  }
27702  }
27703  }
27704 
27705  if (haveUpper && !haveLower)
27706  {
27707  range.lower = range.upper;
27708  haveLower = true;
27709  } else if (haveLower && !haveUpper)
27710  {
27711  range.upper = range.lower;
27712  haveUpper = true;
27713  }
27714 
27715  foundRange = haveLower && haveUpper;
27716  return range;
27717 }
27718 
27730 void QCPErrorBars::getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it, QVector<QLineF> &backbones, QVector<QLineF> &whiskers) const
27731 {
27732  if (!mDataPlottable) return;
27733 
27734  int index = it-mDataContainer->constBegin();
27735  QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index);
27736  if (qIsNaN(centerPixel.x()) || qIsNaN(centerPixel.y()))
27737  return;
27738  QCPAxis *errorAxis = mErrorType == etValueError ? mValueAxis.data() : mKeyAxis.data();
27739  QCPAxis *orthoAxis = mErrorType == etValueError ? mKeyAxis.data() : mValueAxis.data();
27740  const double centerErrorAxisPixel = errorAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();
27741  const double centerOrthoAxisPixel = orthoAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();
27742  const double centerErrorAxisCoord = errorAxis->pixelToCoord(centerErrorAxisPixel); // depending on plottable, this might be different from just mDataPlottable->interface1D()->dataMainKey/Value
27743  const double symbolGap = mSymbolGap*0.5*errorAxis->pixelOrientation();
27744  // plus error:
27745  double errorStart, errorEnd;
27746  if (!qIsNaN(it->errorPlus))
27747  {
27748  errorStart = centerErrorAxisPixel+symbolGap;
27749  errorEnd = errorAxis->coordToPixel(centerErrorAxisCoord+it->errorPlus);
27750  if (errorAxis->orientation() == Qt::Vertical)
27751  {
27752  if ((errorStart > errorEnd) != errorAxis->rangeReversed())
27753  backbones.append(QLineF(centerOrthoAxisPixel, errorStart, centerOrthoAxisPixel, errorEnd));
27754  whiskers.append(QLineF(centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5, errorEnd));
27755  } else
27756  {
27757  if ((errorStart < errorEnd) != errorAxis->rangeReversed())
27758  backbones.append(QLineF(errorStart, centerOrthoAxisPixel, errorEnd, centerOrthoAxisPixel));
27759  whiskers.append(QLineF(errorEnd, centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5));
27760  }
27761  }
27762  // minus error:
27763  if (!qIsNaN(it->errorMinus))
27764  {
27765  errorStart = centerErrorAxisPixel-symbolGap;
27766  errorEnd = errorAxis->coordToPixel(centerErrorAxisCoord-it->errorMinus);
27767  if (errorAxis->orientation() == Qt::Vertical)
27768  {
27769  if ((errorStart < errorEnd) != errorAxis->rangeReversed())
27770  backbones.append(QLineF(centerOrthoAxisPixel, errorStart, centerOrthoAxisPixel, errorEnd));
27771  whiskers.append(QLineF(centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5, errorEnd));
27772  } else
27773  {
27774  if ((errorStart > errorEnd) != errorAxis->rangeReversed())
27775  backbones.append(QLineF(errorStart, centerOrthoAxisPixel, errorEnd, centerOrthoAxisPixel));
27776  whiskers.append(QLineF(errorEnd, centerOrthoAxisPixel-mWhiskerWidth*0.5, errorEnd, centerOrthoAxisPixel+mWhiskerWidth*0.5));
27777  }
27778  }
27779 }
27780 
27799 void QCPErrorBars::getVisibleDataBounds(QCPErrorBarsDataContainer::const_iterator &begin, QCPErrorBarsDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
27800 {
27801  QCPAxis *keyAxis = mKeyAxis.data();
27802  QCPAxis *valueAxis = mValueAxis.data();
27803  if (!keyAxis || !valueAxis)
27804  {
27805  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
27806  end = mDataContainer->constEnd();
27807  begin = end;
27808  return;
27809  }
27810  if (!mDataPlottable || rangeRestriction.isEmpty())
27811  {
27812  end = mDataContainer->constEnd();
27813  begin = end;
27814  return;
27815  }
27816  if (!mDataPlottable->interface1D()->sortKeyIsMainKey())
27817  {
27818  // if the sort key isn't the main key, it's not possible to find a contiguous range of visible
27819  // data points, so this method then only applies the range restriction and otherwise returns
27820  // the full data range. Visibility checks must be done on a per-datapoin-basis during drawing
27821  QCPDataRange dataRange(0, mDataContainer->size());
27822  dataRange = dataRange.bounded(rangeRestriction);
27823  begin = mDataContainer->constBegin()+dataRange.begin();
27824  end = mDataContainer->constBegin()+dataRange.end();
27825  return;
27826  }
27827 
27828  // get visible data range via interface from data plottable, and then restrict to available error data points:
27829  const int n = qMin(mDataContainer->size(), mDataPlottable->interface1D()->dataCount());
27830  int beginIndex = mDataPlottable->interface1D()->findBegin(keyAxis->range().lower);
27831  int endIndex = mDataPlottable->interface1D()->findEnd(keyAxis->range().upper);
27832  int i = beginIndex;
27833  while (i > 0 && i < n && i > rangeRestriction.begin())
27834  {
27835  if (errorBarVisible(i))
27836  beginIndex = i;
27837  --i;
27838  }
27839  i = endIndex;
27840  while (i >= 0 && i < n && i < rangeRestriction.end())
27841  {
27842  if (errorBarVisible(i))
27843  endIndex = i+1;
27844  ++i;
27845  }
27846  QCPDataRange dataRange(beginIndex, endIndex);
27847  dataRange = dataRange.bounded(rangeRestriction.bounded(QCPDataRange(0, mDataContainer->size())));
27848  begin = mDataContainer->constBegin()+dataRange.begin();
27849  end = mDataContainer->constBegin()+dataRange.end();
27850 }
27851 
27858 double QCPErrorBars::pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataContainer::const_iterator &closestData) const
27859 {
27860  closestData = mDataContainer->constEnd();
27861  if (!mDataPlottable || mDataContainer->isEmpty())
27862  return -1.0;
27863  if (!mKeyAxis || !mValueAxis)
27864  {
27865  qDebug() << Q_FUNC_INFO << "invalid key or value axis";
27866  return -1.0;
27867  }
27868 
27869  QCPErrorBarsDataContainer::const_iterator begin, end;
27870  getVisibleDataBounds(begin, end, QCPDataRange(0, dataCount()));
27871 
27872  // calculate minimum distances to error backbones (whiskers are ignored for speed) and find closestData iterator:
27873  double minDistSqr = std::numeric_limits<double>::max();
27874  QVector<QLineF> backbones, whiskers;
27875  for (QCPErrorBarsDataContainer::const_iterator it=begin; it!=end; ++it)
27876  {
27877  getErrorBarLines(it, backbones, whiskers);
27878  for (int i=0; i<backbones.size(); ++i)
27879  {
27880  const double currentDistSqr = QCPVector2D(pixelPoint).distanceSquaredToLine(backbones.at(i));
27881  if (currentDistSqr < minDistSqr)
27882  {
27883  minDistSqr = currentDistSqr;
27884  closestData = it;
27885  }
27886  }
27887  }
27888  return qSqrt(minDistSqr);
27889 }
27890 
27898 void QCPErrorBars::getDataSegments(QList<QCPDataRange> &selectedSegments, QList<QCPDataRange> &unselectedSegments) const
27899 {
27900  selectedSegments.clear();
27901  unselectedSegments.clear();
27902  if (mSelectable == QCP::stWhole) // stWhole selection type draws the entire plottable with selected style if mSelection isn't empty
27903  {
27904  if (selected())
27905  selectedSegments << QCPDataRange(0, dataCount());
27906  else
27907  unselectedSegments << QCPDataRange(0, dataCount());
27908  } else
27909  {
27910  QCPDataSelection sel(selection());
27911  sel.simplify();
27912  selectedSegments = sel.dataRanges();
27913  unselectedSegments = sel.inverse(QCPDataRange(0, dataCount())).dataRanges();
27914  }
27915 }
27916 
27926 bool QCPErrorBars::errorBarVisible(int index) const
27927 {
27928  QPointF centerPixel = mDataPlottable->interface1D()->dataPixelPosition(index);
27929  const double centerKeyPixel = mKeyAxis->orientation() == Qt::Horizontal ? centerPixel.x() : centerPixel.y();
27930  if (qIsNaN(centerKeyPixel))
27931  return false;
27932 
27933  double keyMin, keyMax;
27934  if (mErrorType == etKeyError)
27935  {
27936  const double centerKey = mKeyAxis->pixelToCoord(centerKeyPixel);
27937  const double errorPlus = mDataContainer->at(index).errorPlus;
27938  const double errorMinus = mDataContainer->at(index).errorMinus;
27939  keyMax = centerKey+(qIsNaN(errorPlus) ? 0 : errorPlus);
27940  keyMin = centerKey-(qIsNaN(errorMinus) ? 0 : errorMinus);
27941  } else // mErrorType == etValueError
27942  {
27943  keyMax = mKeyAxis->pixelToCoord(centerKeyPixel+mWhiskerWidth*0.5*mKeyAxis->pixelOrientation());
27944  keyMin = mKeyAxis->pixelToCoord(centerKeyPixel-mWhiskerWidth*0.5*mKeyAxis->pixelOrientation());
27945  }
27946  return ((keyMax > mKeyAxis->range().lower) && (keyMin < mKeyAxis->range().upper));
27947 }
27948 
27956 bool QCPErrorBars::rectIntersectsLine(const QRectF &pixelRect, const QLineF &line) const
27957 {
27958  if (pixelRect.left() > line.x1() && pixelRect.left() > line.x2())
27959  return false;
27960  else if (pixelRect.right() < line.x1() && pixelRect.right() < line.x2())
27961  return false;
27962  else if (pixelRect.top() > line.y1() && pixelRect.top() > line.y2())
27963  return false;
27964  else if (pixelRect.bottom() < line.y1() && pixelRect.bottom() < line.y2())
27965  return false;
27966  else
27967  return true;
27968 }
27969 /* end of 'src/plottables/plottable-errorbar.cpp' */
27970 
27971 
27972 /* including file 'src/items/item-straightline.cpp', size 7592 */
27973 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
27974 
27978 
27994  QCPAbstractItem(parentPlot),
27995  point1(createPosition(QLatin1String("point1"))),
27996  point2(createPosition(QLatin1String("point2")))
27997 {
27998  point1->setCoords(0, 0);
27999  point2->setCoords(1, 1);
28000 
28001  setPen(QPen(Qt::black));
28002  setSelectedPen(QPen(Qt::blue,2));
28003 }
28004 
28006 {
28007 }
28008 
28015 {
28016  mPen = pen;
28017 }
28018 
28025 {
28026  mSelectedPen = pen;
28027 }
28028 
28029 /* inherits documentation from base class */
28030 double QCPItemStraightLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28031 {
28032  Q_UNUSED(details)
28033  if (onlySelectable && !mSelectable)
28034  return -1;
28035 
28037 }
28038 
28039 /* inherits documentation from base class */
28041 {
28042  QCPVector2D start(point1->pixelPosition());
28044  // get visible segment of straight line inside clipRect:
28045  double clipPad = mainPen().widthF();
28046  QLineF line = getRectClippedStraightLine(start, end-start, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
28047  // paint visible segment, if existent:
28048  if (!line.isNull())
28049  {
28050  painter->setPen(mainPen());
28051  painter->drawLine(line);
28052  }
28053 }
28054 
28062 QLineF QCPItemStraightLine::getRectClippedStraightLine(const QCPVector2D &base, const QCPVector2D &vec, const QRect &rect) const
28063 {
28064  double bx, by;
28065  double gamma;
28066  QLineF result;
28067  if (vec.x() == 0 && vec.y() == 0)
28068  return result;
28069  if (qFuzzyIsNull(vec.x())) // line is vertical
28070  {
28071  // check top of rect:
28072  bx = rect.left();
28073  by = rect.top();
28074  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
28075  if (gamma >= 0 && gamma <= rect.width())
28076  result.setLine(bx+gamma, rect.top(), bx+gamma, rect.bottom()); // no need to check bottom because we know line is vertical
28077  } else if (qFuzzyIsNull(vec.y())) // line is horizontal
28078  {
28079  // check left of rect:
28080  bx = rect.left();
28081  by = rect.top();
28082  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
28083  if (gamma >= 0 && gamma <= rect.height())
28084  result.setLine(rect.left(), by+gamma, rect.right(), by+gamma); // no need to check right because we know line is horizontal
28085  } else // line is skewed
28086  {
28087  QList<QCPVector2D> pointVectors;
28088  // check top of rect:
28089  bx = rect.left();
28090  by = rect.top();
28091  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
28092  if (gamma >= 0 && gamma <= rect.width())
28093  pointVectors.append(QCPVector2D(bx+gamma, by));
28094  // check bottom of rect:
28095  bx = rect.left();
28096  by = rect.bottom();
28097  gamma = base.x()-bx + (by-base.y())*vec.x()/vec.y();
28098  if (gamma >= 0 && gamma <= rect.width())
28099  pointVectors.append(QCPVector2D(bx+gamma, by));
28100  // check left of rect:
28101  bx = rect.left();
28102  by = rect.top();
28103  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
28104  if (gamma >= 0 && gamma <= rect.height())
28105  pointVectors.append(QCPVector2D(bx, by+gamma));
28106  // check right of rect:
28107  bx = rect.right();
28108  by = rect.top();
28109  gamma = base.y()-by + (bx-base.x())*vec.y()/vec.x();
28110  if (gamma >= 0 && gamma <= rect.height())
28111  pointVectors.append(QCPVector2D(bx, by+gamma));
28112 
28113  // evaluate points:
28114  if (pointVectors.size() == 2)
28115  {
28116  result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
28117  } else if (pointVectors.size() > 2)
28118  {
28119  // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
28120  double distSqrMax = 0;
28121  QCPVector2D pv1, pv2;
28122  for (int i=0; i<pointVectors.size()-1; ++i)
28123  {
28124  for (int k=i+1; k<pointVectors.size(); ++k)
28125  {
28126  double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
28127  if (distSqr > distSqrMax)
28128  {
28129  pv1 = pointVectors.at(i);
28130  pv2 = pointVectors.at(k);
28131  distSqrMax = distSqr;
28132  }
28133  }
28134  }
28135  result.setPoints(pv1.toPointF(), pv2.toPointF());
28136  }
28137  }
28138  return result;
28139 }
28140 
28147 {
28148  return mSelected ? mSelectedPen : mPen;
28149 }
28150 /* end of 'src/items/item-straightline.cpp' */
28151 
28152 
28153 /* including file 'src/items/item-line.cpp', size 8498 */
28154 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
28155 
28159 
28177  QCPAbstractItem(parentPlot),
28178  start(createPosition(QLatin1String("start"))),
28179  end(createPosition(QLatin1String("end")))
28180 {
28181  start->setCoords(0, 0);
28182  end->setCoords(1, 1);
28183 
28184  setPen(QPen(Qt::black));
28185  setSelectedPen(QPen(Qt::blue,2));
28186 }
28187 
28189 {
28190 }
28191 
28197 void QCPItemLine::setPen(const QPen &pen)
28198 {
28199  mPen = pen;
28200 }
28201 
28208 {
28209  mSelectedPen = pen;
28210 }
28211 
28221 {
28222  mHead = head;
28223 }
28224 
28234 {
28235  mTail = tail;
28236 }
28237 
28238 /* inherits documentation from base class */
28239 double QCPItemLine::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28240 {
28241  Q_UNUSED(details)
28242  if (onlySelectable && !mSelectable)
28243  return -1;
28244 
28245  return qSqrt(QCPVector2D(pos).distanceSquaredToLine(start->pixelPosition(), end->pixelPosition()));
28246 }
28247 
28248 /* inherits documentation from base class */
28250 {
28251  QCPVector2D startVec(start->pixelPosition());
28252  QCPVector2D endVec(end->pixelPosition());
28253  if (qFuzzyIsNull((startVec-endVec).lengthSquared()))
28254  return;
28255  // get visible segment of straight line inside clipRect:
28256  double clipPad = qMax(mHead.boundingDistance(), mTail.boundingDistance());
28257  clipPad = qMax(clipPad, (double)mainPen().widthF());
28258  QLineF line = getRectClippedLine(startVec, endVec, clipRect().adjusted(-clipPad, -clipPad, clipPad, clipPad));
28259  // paint visible segment, if existent:
28260  if (!line.isNull())
28261  {
28262  painter->setPen(mainPen());
28263  painter->drawLine(line);
28264  painter->setBrush(Qt::SolidPattern);
28266  mTail.draw(painter, startVec, startVec-endVec);
28268  mHead.draw(painter, endVec, endVec-startVec);
28269  }
28270 }
28271 
28279 QLineF QCPItemLine::getRectClippedLine(const QCPVector2D &start, const QCPVector2D &end, const QRect &rect) const
28280 {
28281  bool containsStart = rect.contains(start.x(), start.y());
28282  bool containsEnd = rect.contains(end.x(), end.y());
28283  if (containsStart && containsEnd)
28284  return QLineF(start.toPointF(), end.toPointF());
28285 
28286  QCPVector2D base = start;
28287  QCPVector2D vec = end-start;
28288  double bx, by;
28289  double gamma, mu;
28290  QLineF result;
28291  QList<QCPVector2D> pointVectors;
28292 
28293  if (!qFuzzyIsNull(vec.y())) // line is not horizontal
28294  {
28295  // check top of rect:
28296  bx = rect.left();
28297  by = rect.top();
28298  mu = (by-base.y())/vec.y();
28299  if (mu >= 0 && mu <= 1)
28300  {
28301  gamma = base.x()-bx + mu*vec.x();
28302  if (gamma >= 0 && gamma <= rect.width())
28303  pointVectors.append(QCPVector2D(bx+gamma, by));
28304  }
28305  // check bottom of rect:
28306  bx = rect.left();
28307  by = rect.bottom();
28308  mu = (by-base.y())/vec.y();
28309  if (mu >= 0 && mu <= 1)
28310  {
28311  gamma = base.x()-bx + mu*vec.x();
28312  if (gamma >= 0 && gamma <= rect.width())
28313  pointVectors.append(QCPVector2D(bx+gamma, by));
28314  }
28315  }
28316  if (!qFuzzyIsNull(vec.x())) // line is not vertical
28317  {
28318  // check left of rect:
28319  bx = rect.left();
28320  by = rect.top();
28321  mu = (bx-base.x())/vec.x();
28322  if (mu >= 0 && mu <= 1)
28323  {
28324  gamma = base.y()-by + mu*vec.y();
28325  if (gamma >= 0 && gamma <= rect.height())
28326  pointVectors.append(QCPVector2D(bx, by+gamma));
28327  }
28328  // check right of rect:
28329  bx = rect.right();
28330  by = rect.top();
28331  mu = (bx-base.x())/vec.x();
28332  if (mu >= 0 && mu <= 1)
28333  {
28334  gamma = base.y()-by + mu*vec.y();
28335  if (gamma >= 0 && gamma <= rect.height())
28336  pointVectors.append(QCPVector2D(bx, by+gamma));
28337  }
28338  }
28339 
28340  if (containsStart)
28341  pointVectors.append(start);
28342  if (containsEnd)
28343  pointVectors.append(end);
28344 
28345  // evaluate points:
28346  if (pointVectors.size() == 2)
28347  {
28348  result.setPoints(pointVectors.at(0).toPointF(), pointVectors.at(1).toPointF());
28349  } else if (pointVectors.size() > 2)
28350  {
28351  // line probably goes through corner of rect, and we got two points there. single out the point pair with greatest distance:
28352  double distSqrMax = 0;
28353  QCPVector2D pv1, pv2;
28354  for (int i=0; i<pointVectors.size()-1; ++i)
28355  {
28356  for (int k=i+1; k<pointVectors.size(); ++k)
28357  {
28358  double distSqr = (pointVectors.at(i)-pointVectors.at(k)).lengthSquared();
28359  if (distSqr > distSqrMax)
28360  {
28361  pv1 = pointVectors.at(i);
28362  pv2 = pointVectors.at(k);
28363  distSqrMax = distSqr;
28364  }
28365  }
28366  }
28367  result.setPoints(pv1.toPointF(), pv2.toPointF());
28368  }
28369  return result;
28370 }
28371 
28378 {
28379  return mSelected ? mSelectedPen : mPen;
28380 }
28381 /* end of 'src/items/item-line.cpp' */
28382 
28383 
28384 /* including file 'src/items/item-curve.cpp', size 7159 */
28385 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
28386 
28390 
28415  QCPAbstractItem(parentPlot),
28416  start(createPosition(QLatin1String("start"))),
28417  startDir(createPosition(QLatin1String("startDir"))),
28418  endDir(createPosition(QLatin1String("endDir"))),
28419  end(createPosition(QLatin1String("end")))
28420 {
28421  start->setCoords(0, 0);
28422  startDir->setCoords(0.5, 0);
28423  endDir->setCoords(0, 0.5);
28424  end->setCoords(1, 1);
28425 
28426  setPen(QPen(Qt::black));
28427  setSelectedPen(QPen(Qt::blue,2));
28428 }
28429 
28431 {
28432 }
28433 
28439 void QCPItemCurve::setPen(const QPen &pen)
28440 {
28441  mPen = pen;
28442 }
28443 
28450 {
28451  mSelectedPen = pen;
28452 }
28453 
28463 {
28464  mHead = head;
28465 }
28466 
28476 {
28477  mTail = tail;
28478 }
28479 
28480 /* inherits documentation from base class */
28481 double QCPItemCurve::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28482 {
28483  Q_UNUSED(details)
28484  if (onlySelectable && !mSelectable)
28485  return -1;
28486 
28487  QPointF startVec(start->pixelPosition());
28488  QPointF startDirVec(startDir->pixelPosition());
28489  QPointF endDirVec(endDir->pixelPosition());
28490  QPointF endVec(end->pixelPosition());
28491 
28492  QPainterPath cubicPath(startVec);
28493  cubicPath.cubicTo(startDirVec, endDirVec, endVec);
28494 
28495  QPolygonF polygon = cubicPath.toSubpathPolygons().first();
28496  QCPVector2D p(pos);
28497  double minDistSqr = std::numeric_limits<double>::max();
28498  for (int i=1; i<polygon.size(); ++i)
28499  {
28500  double distSqr = p.distanceSquaredToLine(polygon.at(i-1), polygon.at(i));
28501  if (distSqr < minDistSqr)
28502  minDistSqr = distSqr;
28503  }
28504  return qSqrt(minDistSqr);
28505 }
28506 
28507 /* inherits documentation from base class */
28509 {
28510  QCPVector2D startVec(start->pixelPosition());
28511  QCPVector2D startDirVec(startDir->pixelPosition());
28512  QCPVector2D endDirVec(endDir->pixelPosition());
28513  QCPVector2D endVec(end->pixelPosition());
28514  if ((endVec-startVec).length() > 1e10) // too large curves cause crash
28515  return;
28516 
28517  QPainterPath cubicPath(startVec.toPointF());
28518  cubicPath.cubicTo(startDirVec.toPointF(), endDirVec.toPointF(), endVec.toPointF());
28519 
28520  // paint visible segment, if existent:
28521  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
28522  QRect cubicRect = cubicPath.controlPointRect().toRect();
28523  if (cubicRect.isEmpty()) // may happen when start and end exactly on same x or y position
28524  cubicRect.adjust(0, 0, 1, 1);
28525  if (clip.intersects(cubicRect))
28526  {
28527  painter->setPen(mainPen());
28528  painter->drawPath(cubicPath);
28529  painter->setBrush(Qt::SolidPattern);
28531  mTail.draw(painter, startVec, M_PI-cubicPath.angleAtPercent(0)/180.0*M_PI);
28533  mHead.draw(painter, endVec, -cubicPath.angleAtPercent(1)/180.0*M_PI);
28534  }
28535 }
28536 
28543 {
28544  return mSelected ? mSelectedPen : mPen;
28545 }
28546 /* end of 'src/items/item-curve.cpp' */
28547 
28548 
28549 /* including file 'src/items/item-rect.cpp', size 6479 */
28550 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
28551 
28555 
28571  QCPAbstractItem(parentPlot),
28572  topLeft(createPosition(QLatin1String("topLeft"))),
28573  bottomRight(createPosition(QLatin1String("bottomRight"))),
28574  top(createAnchor(QLatin1String("top"), aiTop)),
28575  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
28576  right(createAnchor(QLatin1String("right"), aiRight)),
28577  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
28578  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
28579  left(createAnchor(QLatin1String("left"), aiLeft))
28580 {
28581  topLeft->setCoords(0, 1);
28582  bottomRight->setCoords(1, 0);
28583 
28584  setPen(QPen(Qt::black));
28585  setSelectedPen(QPen(Qt::blue,2));
28586  setBrush(Qt::NoBrush);
28587  setSelectedBrush(Qt::NoBrush);
28588 }
28589 
28591 {
28592 }
28593 
28599 void QCPItemRect::setPen(const QPen &pen)
28600 {
28601  mPen = pen;
28602 }
28603 
28610 {
28611  mSelectedPen = pen;
28612 }
28613 
28620 void QCPItemRect::setBrush(const QBrush &brush)
28621 {
28622  mBrush = brush;
28623 }
28624 
28632 {
28634 }
28635 
28636 /* inherits documentation from base class */
28637 double QCPItemRect::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28638 {
28639  Q_UNUSED(details)
28640  if (onlySelectable && !mSelectable)
28641  return -1;
28642 
28643  QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition()).normalized();
28644  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
28645  return rectDistance(rect, pos, filledRect);
28646 }
28647 
28648 /* inherits documentation from base class */
28650 {
28651  QPointF p1 = topLeft->pixelPosition();
28652  QPointF p2 = bottomRight->pixelPosition();
28653  if (p1.toPoint() == p2.toPoint())
28654  return;
28655  QRectF rect = QRectF(p1, p2).normalized();
28656  double clipPad = mainPen().widthF();
28657  QRectF boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
28658  if (boundingRect.intersects(clipRect())) // only draw if bounding rect of rect item is visible in cliprect
28659  {
28660  painter->setPen(mainPen());
28661  painter->setBrush(mainBrush());
28662  painter->drawRect(rect);
28663  }
28664 }
28665 
28666 /* inherits documentation from base class */
28667 QPointF QCPItemRect::anchorPixelPosition(int anchorId) const
28668 {
28669  QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition());
28670  switch (anchorId)
28671  {
28672  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
28673  case aiTopRight: return rect.topRight();
28674  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
28675  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
28676  case aiBottomLeft: return rect.bottomLeft();
28677  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
28678  }
28679 
28680  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
28681  return QPointF();
28682 }
28683 
28690 {
28691  return mSelected ? mSelectedPen : mPen;
28692 }
28693 
28700 {
28701  return mSelected ? mSelectedBrush : mBrush;
28702 }
28703 /* end of 'src/items/item-rect.cpp' */
28704 
28705 
28706 /* including file 'src/items/item-text.cpp', size 13338 */
28707 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
28708 
28712 
28734  QCPAbstractItem(parentPlot),
28735  position(createPosition(QLatin1String("position"))),
28736  topLeft(createAnchor(QLatin1String("topLeft"), aiTopLeft)),
28737  top(createAnchor(QLatin1String("top"), aiTop)),
28738  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
28739  right(createAnchor(QLatin1String("right"), aiRight)),
28740  bottomRight(createAnchor(QLatin1String("bottomRight"), aiBottomRight)),
28741  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
28742  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
28743  left(createAnchor(QLatin1String("left"), aiLeft)),
28744  mText(QLatin1String("text")),
28745  mPositionAlignment(Qt::AlignCenter),
28746  mTextAlignment(Qt::AlignTop|Qt::AlignHCenter),
28747  mRotation(0)
28748 {
28749  position->setCoords(0, 0);
28750 
28751  setPen(Qt::NoPen);
28752  setSelectedPen(Qt::NoPen);
28753  setBrush(Qt::NoBrush);
28754  setSelectedBrush(Qt::NoBrush);
28755  setColor(Qt::black);
28756  setSelectedColor(Qt::blue);
28757 }
28758 
28760 {
28761 }
28762 
28766 void QCPItemText::setColor(const QColor &color)
28767 {
28768  mColor = color;
28769 }
28770 
28775 {
28777 }
28778 
28785 void QCPItemText::setPen(const QPen &pen)
28786 {
28787  mPen = pen;
28788 }
28789 
28797 {
28798  mSelectedPen = pen;
28799 }
28800 
28807 void QCPItemText::setBrush(const QBrush &brush)
28808 {
28809  mBrush = brush;
28810 }
28811 
28819 {
28821 }
28822 
28828 void QCPItemText::setFont(const QFont &font)
28829 {
28830  mFont = font;
28831 }
28832 
28839 {
28840  mSelectedFont = font;
28841 }
28842 
28849 void QCPItemText::setText(const QString &text)
28850 {
28851  mText = text;
28852 }
28853 
28866 void QCPItemText::setPositionAlignment(Qt::Alignment alignment)
28867 {
28868  mPositionAlignment = alignment;
28869 }
28870 
28874 void QCPItemText::setTextAlignment(Qt::Alignment alignment)
28875 {
28876  mTextAlignment = alignment;
28877 }
28878 
28883 void QCPItemText::setRotation(double degrees)
28884 {
28885  mRotation = degrees;
28886 }
28887 
28892 void QCPItemText::setPadding(const QMargins &padding)
28893 {
28894  mPadding = padding;
28895 }
28896 
28897 /* inherits documentation from base class */
28898 double QCPItemText::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
28899 {
28900  Q_UNUSED(details)
28901  if (onlySelectable && !mSelectable)
28902  return -1;
28903 
28904  // The rect may be rotated, so we transform the actual clicked pos to the rotated
28905  // coordinate system, so we can use the normal rectDistance function for non-rotated rects:
28906  QPointF positionPixels(position->pixelPosition());
28907  QTransform inputTransform;
28908  inputTransform.translate(positionPixels.x(), positionPixels.y());
28909  inputTransform.rotate(-mRotation);
28910  inputTransform.translate(-positionPixels.x(), -positionPixels.y());
28911  QPointF rotatedPos = inputTransform.map(pos);
28912  QFontMetrics fontMetrics(mFont);
28913  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
28914  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
28915  QPointF textPos = getTextDrawPoint(positionPixels, textBoxRect, mPositionAlignment);
28916  textBoxRect.moveTopLeft(textPos.toPoint());
28917 
28918  return rectDistance(textBoxRect, rotatedPos, true);
28919 }
28920 
28921 /* inherits documentation from base class */
28923 {
28924  QPointF pos(position->pixelPosition());
28925  QTransform transform = painter->transform();
28926  transform.translate(pos.x(), pos.y());
28927  if (!qFuzzyIsNull(mRotation))
28928  transform.rotate(mRotation);
28929  painter->setFont(mainFont());
28930  QRect textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
28931  QRect textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
28932  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
28933  textRect.moveTopLeft(textPos.toPoint()+QPoint(mPadding.left(), mPadding.top()));
28934  textBoxRect.moveTopLeft(textPos.toPoint());
28935  double clipPad = mainPen().widthF();
28936  QRect boundingRect = textBoxRect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
28937  if (transform.mapRect(boundingRect).intersects(painter->transform().mapRect(clipRect())))
28938  {
28939  painter->setTransform(transform);
28940  if ((mainBrush().style() != Qt::NoBrush && mainBrush().color().alpha() != 0) ||
28941  (mainPen().style() != Qt::NoPen && mainPen().color().alpha() != 0))
28942  {
28943  painter->setPen(mainPen());
28944  painter->setBrush(mainBrush());
28945  painter->drawRect(textBoxRect);
28946  }
28947  painter->setBrush(Qt::NoBrush);
28948  painter->setPen(QPen(mainColor()));
28949  painter->drawText(textRect, Qt::TextDontClip|mTextAlignment, mText);
28950  }
28951 }
28952 
28953 /* inherits documentation from base class */
28954 QPointF QCPItemText::anchorPixelPosition(int anchorId) const
28955 {
28956  // get actual rect points (pretty much copied from draw function):
28957  QPointF pos(position->pixelPosition());
28958  QTransform transform;
28959  transform.translate(pos.x(), pos.y());
28960  if (!qFuzzyIsNull(mRotation))
28961  transform.rotate(mRotation);
28962  QFontMetrics fontMetrics(mainFont());
28963  QRect textRect = fontMetrics.boundingRect(0, 0, 0, 0, Qt::TextDontClip|mTextAlignment, mText);
28964  QRectF textBoxRect = textRect.adjusted(-mPadding.left(), -mPadding.top(), mPadding.right(), mPadding.bottom());
28965  QPointF textPos = getTextDrawPoint(QPointF(0, 0), textBoxRect, mPositionAlignment); // 0, 0 because the transform does the translation
28966  textBoxRect.moveTopLeft(textPos.toPoint());
28967  QPolygonF rectPoly = transform.map(QPolygonF(textBoxRect));
28968 
28969  switch (anchorId)
28970  {
28971  case aiTopLeft: return rectPoly.at(0);
28972  case aiTop: return (rectPoly.at(0)+rectPoly.at(1))*0.5;
28973  case aiTopRight: return rectPoly.at(1);
28974  case aiRight: return (rectPoly.at(1)+rectPoly.at(2))*0.5;
28975  case aiBottomRight: return rectPoly.at(2);
28976  case aiBottom: return (rectPoly.at(2)+rectPoly.at(3))*0.5;
28977  case aiBottomLeft: return rectPoly.at(3);
28978  case aiLeft: return (rectPoly.at(3)+rectPoly.at(0))*0.5;
28979  }
28980 
28981  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
28982  return QPointF();
28983 }
28984 
28995 QPointF QCPItemText::getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
28996 {
28997  if (positionAlignment == 0 || positionAlignment == (Qt::AlignLeft|Qt::AlignTop))
28998  return pos;
28999 
29000  QPointF result = pos; // start at top left
29001  if (positionAlignment.testFlag(Qt::AlignHCenter))
29002  result.rx() -= rect.width()/2.0;
29003  else if (positionAlignment.testFlag(Qt::AlignRight))
29004  result.rx() -= rect.width();
29005  if (positionAlignment.testFlag(Qt::AlignVCenter))
29006  result.ry() -= rect.height()/2.0;
29007  else if (positionAlignment.testFlag(Qt::AlignBottom))
29008  result.ry() -= rect.height();
29009  return result;
29010 }
29011 
29018 {
29019  return mSelected ? mSelectedFont : mFont;
29020 }
29021 
29028 {
29029  return mSelected ? mSelectedColor : mColor;
29030 }
29031 
29038 {
29039  return mSelected ? mSelectedPen : mPen;
29040 }
29041 
29048 {
29049  return mSelected ? mSelectedBrush : mBrush;
29050 }
29051 /* end of 'src/items/item-text.cpp' */
29052 
29053 
29054 /* including file 'src/items/item-ellipse.cpp', size 7863 */
29055 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
29056 
29060 
29076  QCPAbstractItem(parentPlot),
29077  topLeft(createPosition(QLatin1String("topLeft"))),
29078  bottomRight(createPosition(QLatin1String("bottomRight"))),
29079  topLeftRim(createAnchor(QLatin1String("topLeftRim"), aiTopLeftRim)),
29080  top(createAnchor(QLatin1String("top"), aiTop)),
29081  topRightRim(createAnchor(QLatin1String("topRightRim"), aiTopRightRim)),
29082  right(createAnchor(QLatin1String("right"), aiRight)),
29083  bottomRightRim(createAnchor(QLatin1String("bottomRightRim"), aiBottomRightRim)),
29084  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
29085  bottomLeftRim(createAnchor(QLatin1String("bottomLeftRim"), aiBottomLeftRim)),
29086  left(createAnchor(QLatin1String("left"), aiLeft)),
29087  center(createAnchor(QLatin1String("center"), aiCenter))
29088 {
29089  topLeft->setCoords(0, 1);
29090  bottomRight->setCoords(1, 0);
29091 
29092  setPen(QPen(Qt::black));
29093  setSelectedPen(QPen(Qt::blue, 2));
29094  setBrush(Qt::NoBrush);
29095  setSelectedBrush(Qt::NoBrush);
29096 }
29097 
29099 {
29100 }
29101 
29107 void QCPItemEllipse::setPen(const QPen &pen)
29108 {
29109  mPen = pen;
29110 }
29111 
29118 {
29119  mSelectedPen = pen;
29120 }
29121 
29128 void QCPItemEllipse::setBrush(const QBrush &brush)
29129 {
29130  mBrush = brush;
29131 }
29132 
29140 {
29142 }
29143 
29144 /* inherits documentation from base class */
29145 double QCPItemEllipse::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
29146 {
29147  Q_UNUSED(details)
29148  if (onlySelectable && !mSelectable)
29149  return -1;
29150 
29151  QPointF p1 = topLeft->pixelPosition();
29152  QPointF p2 = bottomRight->pixelPosition();
29153  QPointF center((p1+p2)/2.0);
29154  double a = qAbs(p1.x()-p2.x())/2.0;
29155  double b = qAbs(p1.y()-p2.y())/2.0;
29156  double x = pos.x()-center.x();
29157  double y = pos.y()-center.y();
29158 
29159  // distance to border:
29160  double c = 1.0/qSqrt(x*x/(a*a)+y*y/(b*b));
29161  double result = qAbs(c-1)*qSqrt(x*x+y*y);
29162  // filled ellipse, allow click inside to count as hit:
29163  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
29164  {
29165  if (x*x/(a*a) + y*y/(b*b) <= 1)
29166  result = mParentPlot->selectionTolerance()*0.99;
29167  }
29168  return result;
29169 }
29170 
29171 /* inherits documentation from base class */
29173 {
29174  QPointF p1 = topLeft->pixelPosition();
29175  QPointF p2 = bottomRight->pixelPosition();
29176  if (p1.toPoint() == p2.toPoint())
29177  return;
29178  QRectF ellipseRect = QRectF(p1, p2).normalized();
29179  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
29180  if (ellipseRect.intersects(clip)) // only draw if bounding rect of ellipse is visible in cliprect
29181  {
29182  painter->setPen(mainPen());
29183  painter->setBrush(mainBrush());
29184 #ifdef __EXCEPTIONS
29185  try // drawEllipse sometimes throws exceptions if ellipse is too big
29186  {
29187 #endif
29188  painter->drawEllipse(ellipseRect);
29189 #ifdef __EXCEPTIONS
29190  } catch (...)
29191  {
29192  qDebug() << Q_FUNC_INFO << "Item too large for memory, setting invisible";
29193  setVisible(false);
29194  }
29195 #endif
29196  }
29197 }
29198 
29199 /* inherits documentation from base class */
29200 QPointF QCPItemEllipse::anchorPixelPosition(int anchorId) const
29201 {
29202  QRectF rect = QRectF(topLeft->pixelPosition(), bottomRight->pixelPosition());
29203  switch (anchorId)
29204  {
29205  case aiTopLeftRim: return rect.center()+(rect.topLeft()-rect.center())*1/qSqrt(2);
29206  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
29207  case aiTopRightRim: return rect.center()+(rect.topRight()-rect.center())*1/qSqrt(2);
29208  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
29209  case aiBottomRightRim: return rect.center()+(rect.bottomRight()-rect.center())*1/qSqrt(2);
29210  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
29211  case aiBottomLeftRim: return rect.center()+(rect.bottomLeft()-rect.center())*1/qSqrt(2);
29212  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;
29213  case aiCenter: return (rect.topLeft()+rect.bottomRight())*0.5;
29214  }
29215 
29216  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
29217  return QPointF();
29218 }
29219 
29226 {
29227  return mSelected ? mSelectedPen : mPen;
29228 }
29229 
29236 {
29237  return mSelected ? mSelectedBrush : mBrush;
29238 }
29239 /* end of 'src/items/item-ellipse.cpp' */
29240 
29241 
29242 /* including file 'src/items/item-pixmap.cpp', size 10615 */
29243 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
29244 
29248 
29270  QCPAbstractItem(parentPlot),
29271  topLeft(createPosition(QLatin1String("topLeft"))),
29272  bottomRight(createPosition(QLatin1String("bottomRight"))),
29273  top(createAnchor(QLatin1String("top"), aiTop)),
29274  topRight(createAnchor(QLatin1String("topRight"), aiTopRight)),
29275  right(createAnchor(QLatin1String("right"), aiRight)),
29276  bottom(createAnchor(QLatin1String("bottom"), aiBottom)),
29277  bottomLeft(createAnchor(QLatin1String("bottomLeft"), aiBottomLeft)),
29278  left(createAnchor(QLatin1String("left"), aiLeft)),
29279  mScaled(false),
29280  mScaledPixmapInvalidated(true),
29281  mAspectRatioMode(Qt::KeepAspectRatio),
29282  mTransformationMode(Qt::SmoothTransformation)
29283 {
29284  topLeft->setCoords(0, 1);
29285  bottomRight->setCoords(1, 0);
29286 
29287  setPen(Qt::NoPen);
29288  setSelectedPen(QPen(Qt::blue));
29289 }
29290 
29292 {
29293 }
29294 
29298 void QCPItemPixmap::setPixmap(const QPixmap &pixmap)
29299 {
29300  mPixmap = pixmap;
29301  mScaledPixmapInvalidated = true;
29302  if (mPixmap.isNull())
29303  qDebug() << Q_FUNC_INFO << "pixmap is null";
29304 }
29305 
29310 void QCPItemPixmap::setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformationMode)
29311 {
29312  mScaled = scaled;
29315  mScaledPixmapInvalidated = true;
29316 }
29317 
29323 void QCPItemPixmap::setPen(const QPen &pen)
29324 {
29325  mPen = pen;
29326 }
29327 
29334 {
29335  mSelectedPen = pen;
29336 }
29337 
29338 /* inherits documentation from base class */
29339 double QCPItemPixmap::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
29340 {
29341  Q_UNUSED(details)
29342  if (onlySelectable && !mSelectable)
29343  return -1;
29344 
29345  return rectDistance(getFinalRect(), pos, true);
29346 }
29347 
29348 /* inherits documentation from base class */
29350 {
29351  bool flipHorz = false;
29352  bool flipVert = false;
29353  QRect rect = getFinalRect(&flipHorz, &flipVert);
29354  double clipPad = mainPen().style() == Qt::NoPen ? 0 : mainPen().widthF();
29355  QRect boundingRect = rect.adjusted(-clipPad, -clipPad, clipPad, clipPad);
29356  if (boundingRect.intersects(clipRect()))
29357  {
29358  updateScaledPixmap(rect, flipHorz, flipVert);
29359  painter->drawPixmap(rect.topLeft(), mScaled ? mScaledPixmap : mPixmap);
29360  QPen pen = mainPen();
29361  if (pen.style() != Qt::NoPen)
29362  {
29363  painter->setPen(pen);
29364  painter->setBrush(Qt::NoBrush);
29365  painter->drawRect(rect);
29366  }
29367  }
29368 }
29369 
29370 /* inherits documentation from base class */
29371 QPointF QCPItemPixmap::anchorPixelPosition(int anchorId) const
29372 {
29373  bool flipHorz;
29374  bool flipVert;
29375  QRect rect = getFinalRect(&flipHorz, &flipVert);
29376  // we actually want denormal rects (negative width/height) here, so restore
29377  // the flipped state:
29378  if (flipHorz)
29379  rect.adjust(rect.width(), 0, -rect.width(), 0);
29380  if (flipVert)
29381  rect.adjust(0, rect.height(), 0, -rect.height());
29382 
29383  switch (anchorId)
29384  {
29385  case aiTop: return (rect.topLeft()+rect.topRight())*0.5;
29386  case aiTopRight: return rect.topRight();
29387  case aiRight: return (rect.topRight()+rect.bottomRight())*0.5;
29388  case aiBottom: return (rect.bottomLeft()+rect.bottomRight())*0.5;
29389  case aiBottomLeft: return rect.bottomLeft();
29390  case aiLeft: return (rect.topLeft()+rect.bottomLeft())*0.5;;
29391  }
29392 
29393  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
29394  return QPointF();
29395 }
29396 
29410 void QCPItemPixmap::updateScaledPixmap(QRect finalRect, bool flipHorz, bool flipVert)
29411 {
29412  if (mPixmap.isNull())
29413  return;
29414 
29415  if (mScaled)
29416  {
29417 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
29418  double devicePixelRatio = mPixmap.devicePixelRatio();
29419 #else
29420  double devicePixelRatio = 1.0;
29421 #endif
29422  if (finalRect.isNull())
29423  finalRect = getFinalRect(&flipHorz, &flipVert);
29424  if (mScaledPixmapInvalidated || finalRect.size() != mScaledPixmap.size()/devicePixelRatio)
29425  {
29426  mScaledPixmap = mPixmap.scaled(finalRect.size()*devicePixelRatio, mAspectRatioMode, mTransformationMode);
29427  if (flipHorz || flipVert)
29428  mScaledPixmap = QPixmap::fromImage(mScaledPixmap.toImage().mirrored(flipHorz, flipVert));
29429 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
29430  mScaledPixmap.setDevicePixelRatio(devicePixelRatio);
29431 #endif
29432  }
29433  } else if (!mScaledPixmap.isNull())
29434  mScaledPixmap = QPixmap();
29435  mScaledPixmapInvalidated = false;
29436 }
29437 
29452 QRect QCPItemPixmap::getFinalRect(bool *flippedHorz, bool *flippedVert) const
29453 {
29454  QRect result;
29455  bool flipHorz = false;
29456  bool flipVert = false;
29457  QPoint p1 = topLeft->pixelPosition().toPoint();
29458  QPoint p2 = bottomRight->pixelPosition().toPoint();
29459  if (p1 == p2)
29460  return QRect(p1, QSize(0, 0));
29461  if (mScaled)
29462  {
29463  QSize newSize = QSize(p2.x()-p1.x(), p2.y()-p1.y());
29464  QPoint topLeft = p1;
29465  if (newSize.width() < 0)
29466  {
29467  flipHorz = true;
29468  newSize.rwidth() *= -1;
29469  topLeft.setX(p2.x());
29470  }
29471  if (newSize.height() < 0)
29472  {
29473  flipVert = true;
29474  newSize.rheight() *= -1;
29475  topLeft.setY(p2.y());
29476  }
29477  QSize scaledSize = mPixmap.size();
29478 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
29479  scaledSize /= mPixmap.devicePixelRatio();
29480  scaledSize.scale(newSize*mPixmap.devicePixelRatio(), mAspectRatioMode);
29481 #else
29482  scaledSize.scale(newSize, mAspectRatioMode);
29483 #endif
29484  result = QRect(topLeft, scaledSize);
29485  } else
29486  {
29487 #ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
29488  result = QRect(p1, mPixmap.size()/mPixmap.devicePixelRatio());
29489 #else
29490  result = QRect(p1, mPixmap.size());
29491 #endif
29492  }
29493  if (flippedHorz)
29494  *flippedHorz = flipHorz;
29495  if (flippedVert)
29496  *flippedVert = flipVert;
29497  return result;
29498 }
29499 
29506 {
29507  return mSelected ? mSelectedPen : mPen;
29508 }
29509 /* end of 'src/items/item-pixmap.cpp' */
29510 
29511 
29512 /* including file 'src/items/item-tracer.cpp', size 14624 */
29513 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
29514 
29518 
29556  QCPAbstractItem(parentPlot),
29557  position(createPosition(QLatin1String("position"))),
29558  mSize(6),
29559  mStyle(tsCrosshair),
29560  mGraph(0),
29561  mGraphKey(0),
29562  mInterpolating(false)
29563 {
29564  position->setCoords(0, 0);
29565 
29566  setBrush(Qt::NoBrush);
29567  setSelectedBrush(Qt::NoBrush);
29568  setPen(QPen(Qt::black));
29569  setSelectedPen(QPen(Qt::blue, 2));
29570 }
29571 
29573 {
29574 }
29575 
29581 void QCPItemTracer::setPen(const QPen &pen)
29582 {
29583  mPen = pen;
29584 }
29585 
29592 {
29593  mSelectedPen = pen;
29594 }
29595 
29601 void QCPItemTracer::setBrush(const QBrush &brush)
29602 {
29603  mBrush = brush;
29604 }
29605 
29612 {
29614 }
29615 
29621 {
29622  mSize = size;
29623 }
29624 
29632 {
29633  mStyle = style;
29634 }
29635 
29647 {
29648  if (graph)
29649  {
29650  if (graph->parentPlot() == mParentPlot)
29651  {
29653  position->setAxes(graph->keyAxis(), graph->valueAxis());
29654  mGraph = graph;
29655  updatePosition();
29656  } else
29657  qDebug() << Q_FUNC_INFO << "graph isn't in same QCustomPlot instance as this item";
29658  } else
29659  {
29660  mGraph = 0;
29661  }
29662 }
29663 
29674 {
29675  mGraphKey = key;
29676 }
29677 
29690 {
29691  mInterpolating = enabled;
29692 }
29693 
29694 /* inherits documentation from base class */
29695 double QCPItemTracer::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
29696 {
29697  Q_UNUSED(details)
29698  if (onlySelectable && !mSelectable)
29699  return -1;
29700 
29701  QPointF center(position->pixelPosition());
29702  double w = mSize/2.0;
29703  QRect clip = clipRect();
29704  switch (mStyle)
29705  {
29706  case tsNone: return -1;
29707  case tsPlus:
29708  {
29709  if (clipRect().intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29710  return qSqrt(qMin(QCPVector2D(pos).distanceSquaredToLine(center+QPointF(-w, 0), center+QPointF(w, 0)),
29711  QCPVector2D(pos).distanceSquaredToLine(center+QPointF(0, -w), center+QPointF(0, w))));
29712  break;
29713  }
29714  case tsCrosshair:
29715  {
29716  return qSqrt(qMin(QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(clip.left(), center.y()), QCPVector2D(clip.right(), center.y())),
29717  QCPVector2D(pos).distanceSquaredToLine(QCPVector2D(center.x(), clip.top()), QCPVector2D(center.x(), clip.bottom()))));
29718  }
29719  case tsCircle:
29720  {
29721  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29722  {
29723  // distance to border:
29724  double centerDist = QCPVector2D(center-pos).length();
29725  double circleLine = w;
29726  double result = qAbs(centerDist-circleLine);
29727  // filled ellipse, allow click inside to count as hit:
29728  if (result > mParentPlot->selectionTolerance()*0.99 && mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0)
29729  {
29730  if (centerDist <= circleLine)
29731  result = mParentPlot->selectionTolerance()*0.99;
29732  }
29733  return result;
29734  }
29735  break;
29736  }
29737  case tsSquare:
29738  {
29739  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29740  {
29741  QRectF rect = QRectF(center-QPointF(w, w), center+QPointF(w, w));
29742  bool filledRect = mBrush.style() != Qt::NoBrush && mBrush.color().alpha() != 0;
29743  return rectDistance(rect, pos, filledRect);
29744  }
29745  break;
29746  }
29747  }
29748  return -1;
29749 }
29750 
29751 /* inherits documentation from base class */
29753 {
29754  updatePosition();
29755  if (mStyle == tsNone)
29756  return;
29757 
29758  painter->setPen(mainPen());
29759  painter->setBrush(mainBrush());
29760  QPointF center(position->pixelPosition());
29761  double w = mSize/2.0;
29762  QRect clip = clipRect();
29763  switch (mStyle)
29764  {
29765  case tsNone: return;
29766  case tsPlus:
29767  {
29768  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29769  {
29770  painter->drawLine(QLineF(center+QPointF(-w, 0), center+QPointF(w, 0)));
29771  painter->drawLine(QLineF(center+QPointF(0, -w), center+QPointF(0, w)));
29772  }
29773  break;
29774  }
29775  case tsCrosshair:
29776  {
29777  if (center.y() > clip.top() && center.y() < clip.bottom())
29778  painter->drawLine(QLineF(clip.left(), center.y(), clip.right(), center.y()));
29779  if (center.x() > clip.left() && center.x() < clip.right())
29780  painter->drawLine(QLineF(center.x(), clip.top(), center.x(), clip.bottom()));
29781  break;
29782  }
29783  case tsCircle:
29784  {
29785  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29786  painter->drawEllipse(center, w, w);
29787  break;
29788  }
29789  case tsSquare:
29790  {
29791  if (clip.intersects(QRectF(center-QPointF(w, w), center+QPointF(w, w)).toRect()))
29792  painter->drawRect(QRectF(center-QPointF(w, w), center+QPointF(w, w)));
29793  break;
29794  }
29795  }
29796 }
29797 
29811 {
29812  if (mGraph)
29813  {
29815  {
29816  if (mGraph->data()->size() > 1)
29817  {
29818  QCPGraphDataContainer::const_iterator first = mGraph->data()->constBegin();
29819  QCPGraphDataContainer::const_iterator last = mGraph->data()->constEnd()-1;
29820  if (mGraphKey <= first->key)
29821  position->setCoords(first->key, first->value);
29822  else if (mGraphKey >= last->key)
29823  position->setCoords(last->key, last->value);
29824  else
29825  {
29827  if (it != mGraph->data()->constEnd()) // mGraphKey is not exactly on last iterator, but somewhere between iterators
29828  {
29830  ++it; // won't advance to constEnd because we handled that case (mGraphKey >= last->key) before
29831  if (mInterpolating)
29832  {
29833  // interpolate between iterators around mGraphKey:
29834  double slope = 0;
29835  if (!qFuzzyCompare((double)it->key, (double)prevIt->key))
29836  slope = (it->value-prevIt->value)/(it->key-prevIt->key);
29837  position->setCoords(mGraphKey, (mGraphKey-prevIt->key)*slope+prevIt->value);
29838  } else
29839  {
29840  // find iterator with key closest to mGraphKey:
29841  if (mGraphKey < (prevIt->key+it->key)*0.5)
29842  position->setCoords(prevIt->key, prevIt->value);
29843  else
29844  position->setCoords(it->key, it->value);
29845  }
29846  } else // mGraphKey is exactly on last iterator (should actually be caught when comparing first/last keys, but this is a failsafe for fp uncertainty)
29847  position->setCoords(it->key, it->value);
29848  }
29849  } else if (mGraph->data()->size() == 1)
29850  {
29851  QCPGraphDataContainer::const_iterator it = mGraph->data()->constBegin();
29852  position->setCoords(it->key, it->value);
29853  } else
29854  qDebug() << Q_FUNC_INFO << "graph has no data";
29855  } else
29856  qDebug() << Q_FUNC_INFO << "graph not contained in QCustomPlot instance (anymore)";
29857  }
29858 }
29859 
29866 {
29867  return mSelected ? mSelectedPen : mPen;
29868 }
29869 
29876 {
29877  return mSelected ? mSelectedBrush : mBrush;
29878 }
29879 /* end of 'src/items/item-tracer.cpp' */
29880 
29881 
29882 /* including file 'src/items/item-bracket.cpp', size 10687 */
29883 /* commit 9868e55d3b412f2f89766bb482fcf299e93a0988 2017-09-04 01:56:22 +0200 */
29884 
29888 
29916  QCPAbstractItem(parentPlot),
29917  left(createPosition(QLatin1String("left"))),
29918  right(createPosition(QLatin1String("right"))),
29919  center(createAnchor(QLatin1String("center"), aiCenter)),
29920  mLength(8),
29921  mStyle(bsCalligraphic)
29922 {
29923  left->setCoords(0, 0);
29924  right->setCoords(1, 1);
29925 
29926  setPen(QPen(Qt::black));
29927  setSelectedPen(QPen(Qt::blue, 2));
29928 }
29929 
29931 {
29932 }
29933 
29943 void QCPItemBracket::setPen(const QPen &pen)
29944 {
29945  mPen = pen;
29946 }
29947 
29954 {
29955  mSelectedPen = pen;
29956 }
29957 
29967 {
29968  mLength = length;
29969 }
29970 
29977 {
29978  mStyle = style;
29979 }
29980 
29981 /* inherits documentation from base class */
29982 double QCPItemBracket::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
29983 {
29984  Q_UNUSED(details)
29985  if (onlySelectable && !mSelectable)
29986  return -1;
29987 
29988  QCPVector2D p(pos);
29989  QCPVector2D leftVec(left->pixelPosition());
29990  QCPVector2D rightVec(right->pixelPosition());
29991  if (leftVec.toPoint() == rightVec.toPoint())
29992  return -1;
29993 
29994  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
29995  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
29996  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
29997 
29998  switch (mStyle)
29999  {
30002  {
30003  double a = p.distanceSquaredToLine(centerVec-widthVec, centerVec+widthVec);
30004  double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec, centerVec-widthVec);
30005  double c = p.distanceSquaredToLine(centerVec+widthVec+lengthVec, centerVec+widthVec);
30006  return qSqrt(qMin(qMin(a, b), c));
30007  }
30010  {
30011  double a = p.distanceSquaredToLine(centerVec-widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3);
30012  double b = p.distanceSquaredToLine(centerVec-widthVec+lengthVec*0.7, centerVec-widthVec*0.75+lengthVec*0.15);
30013  double c = p.distanceSquaredToLine(centerVec+widthVec*0.75+lengthVec*0.15, centerVec+lengthVec*0.3);
30014  double d = p.distanceSquaredToLine(centerVec+widthVec+lengthVec*0.7, centerVec+widthVec*0.75+lengthVec*0.15);
30015  return qSqrt(qMin(qMin(a, b), qMin(c, d)));
30016  }
30017  }
30018  return -1;
30019 }
30020 
30021 /* inherits documentation from base class */
30023 {
30024  QCPVector2D leftVec(left->pixelPosition());
30025  QCPVector2D rightVec(right->pixelPosition());
30026  if (leftVec.toPoint() == rightVec.toPoint())
30027  return;
30028 
30029  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
30030  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
30031  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
30032 
30033  QPolygon boundingPoly;
30034  boundingPoly << leftVec.toPoint() << rightVec.toPoint()
30035  << (rightVec-lengthVec).toPoint() << (leftVec-lengthVec).toPoint();
30036  QRect clip = clipRect().adjusted(-mainPen().widthF(), -mainPen().widthF(), mainPen().widthF(), mainPen().widthF());
30037  if (clip.intersects(boundingPoly.boundingRect()))
30038  {
30039  painter->setPen(mainPen());
30040  switch (mStyle)
30041  {
30042  case bsSquare:
30043  {
30044  painter->drawLine((centerVec+widthVec).toPointF(), (centerVec-widthVec).toPointF());
30045  painter->drawLine((centerVec+widthVec).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
30046  painter->drawLine((centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
30047  break;
30048  }
30049  case bsRound:
30050  {
30051  painter->setBrush(Qt::NoBrush);
30052  QPainterPath path;
30053  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
30054  path.cubicTo((centerVec+widthVec).toPointF(), (centerVec+widthVec).toPointF(), centerVec.toPointF());
30055  path.cubicTo((centerVec-widthVec).toPointF(), (centerVec-widthVec).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
30056  painter->drawPath(path);
30057  break;
30058  }
30059  case bsCurly:
30060  {
30061  painter->setBrush(Qt::NoBrush);
30062  QPainterPath path;
30063  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
30064  path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+lengthVec).toPointF(), centerVec.toPointF());
30065  path.cubicTo((centerVec-0.4*widthVec+lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
30066  painter->drawPath(path);
30067  break;
30068  }
30069  case bsCalligraphic:
30070  {
30071  painter->setPen(Qt::NoPen);
30072  painter->setBrush(QBrush(mainPen().color()));
30073  QPainterPath path;
30074  path.moveTo((centerVec+widthVec+lengthVec).toPointF());
30075 
30076  path.cubicTo((centerVec+widthVec-lengthVec*0.8).toPointF(), (centerVec+0.4*widthVec+0.8*lengthVec).toPointF(), centerVec.toPointF());
30077  path.cubicTo((centerVec-0.4*widthVec+0.8*lengthVec).toPointF(), (centerVec-widthVec-lengthVec*0.8).toPointF(), (centerVec-widthVec+lengthVec).toPointF());
30078 
30079  path.cubicTo((centerVec-widthVec-lengthVec*0.5).toPointF(), (centerVec-0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+lengthVec*0.2).toPointF());
30080  path.cubicTo((centerVec+0.2*widthVec+1.2*lengthVec).toPointF(), (centerVec+widthVec-lengthVec*0.5).toPointF(), (centerVec+widthVec+lengthVec).toPointF());
30081 
30082  painter->drawPath(path);
30083  break;
30084  }
30085  }
30086  }
30087 }
30088 
30089 /* inherits documentation from base class */
30090 QPointF QCPItemBracket::anchorPixelPosition(int anchorId) const
30091 {
30092  QCPVector2D leftVec(left->pixelPosition());
30093  QCPVector2D rightVec(right->pixelPosition());
30094  if (leftVec.toPoint() == rightVec.toPoint())
30095  return leftVec.toPointF();
30096 
30097  QCPVector2D widthVec = (rightVec-leftVec)*0.5;
30098  QCPVector2D lengthVec = widthVec.perpendicular().normalized()*mLength;
30099  QCPVector2D centerVec = (rightVec+leftVec)*0.5-lengthVec;
30100 
30101  switch (anchorId)
30102  {
30103  case aiCenter:
30104  return centerVec.toPointF();
30105  }
30106  qDebug() << Q_FUNC_INFO << "invalid anchorId" << anchorId;
30107  return QPointF();
30108 }
30109 
30116 {
30117  return mSelected ? mSelectedPen : mPen;
30118 }
30119 /* end of 'src/items/item-bracket.cpp' */
30120 
30121 
double devicePixelRatio() const
Definition: qcustomplot.h:532
double pointDistance(const QPointF &pixelPoint, QCPGraphDataContainer::const_iterator &closestData) const
QList< QList< QCPLayoutElement * > > mElements
Definition: qcustomplot.h:1404
QSharedPointer< QCPCurveDataContainer > data() const
Definition: qcustomplot.h:5323
QCP::AntialiasedElements mNotAADragBackup
Definition: qcustomplot.h:4684
double cleanMantissa(double input) const
double size() const
Definition: qcustomplot.h:802
void setType(PositionType type)
QCPLayoutElement(QCustomPlot *parentPlot=0)
QPen basePen() const
Definition: qcustomplot.h:2036
QCPColorScaleAxisRectPrivate(QCPColorScale *parentColorScale)
void itemClick(QCPAbstractItem *item, QMouseEvent *event)
void drawSubGridLines(QCPPainter *painter) const
Q_SLOT void setSelection(QCPDataSelection selection)
QPen mSelectedPen
Definition: qcustomplot.h:6273
QList< QSharedPointer< QCPAbstractPaintBuffer > > mPaintBuffers
Definition: qcustomplot.h:3789
void addElement(QCPLayoutElement *element, Qt::Alignment alignment)
bool rangeZoom() const
QCPItemPosition * position(const QString &name) const
void insertRow(int newIndex)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QVector< QPointF > dataToStepLeftLines(const QVector< QCPGraphData > &data) const
SizeConstraintRect sizeConstraintRect() const
Definition: qcustomplot.h:1236
A margin group allows synchronization of margin sides if working with multiple layout elements...
Definition: qcustomplot.h:1156
One individual data point can be selected at a time.
Definition: qcustomplot.h:301
QList< QCPGraph * > mGraphs
Definition: qcustomplot.h:3769
QCPGraph * addGraph(QCPAxis *keyAxis=0, QCPAxis *valueAxis=0)
virtual ~QCPAxisPainterPrivate()
void replot()
Holds the data of one single error bar for QCPErrorBars.
Definition: qcustomplot.h:5963
QCPStatisticalBox(QCPAxis *keyAxis, QCPAxis *valueAxis)
void setSubTickLengthIn(int inside)
QMargins padding() const
Definition: qcustomplot.h:6326
void setMargins(const QMargins &margins)
0x04 Turns pen widths 0 to 1, i.e. disables cosmetic pens. (A cosmetic pen is always drawn with width...
Definition: qcustomplot.h:475
void setBracketBrush(const QBrush &brush)
QCPDataRange intersection(const QCPDataRange &other) const
bool stopsUseAlpha() const
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE
0xFFFF All elements
Definition: qcustomplot.h:233
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QList< QCPLayoutElement * > elements(QCP::MarginSide side) const
Definition: qcustomplot.h:1164
void setWhiskerBarPen(const QPen &pen)
double bufferDevicePixelRatio() const
Definition: qcustomplot.h:3633
int clearPlottables()
WidthType mWidthType
Definition: qcustomplot.h:5934
bool savePng(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch)
void setNotAntialiasedElement(QCP::AntialiasedElement notAntialiasedElement, bool enabled=true)
Qt::Orientations mRangeDrag
Definition: qcustomplot.h:4677
A plus shaped crosshair which spans the complete axis rect.
Definition: qcustomplot.h:6541
virtual void updateLayout()
static const double maxRange
Definition: qcustomplot.h:817
void setPeriodic(bool enabled)
void setLevelCount(int n)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
QRgb color(double position, const QCPRange &range, bool logarithmic=false)
void setTimeFormat(const QString &format)
void setInsetPlacement(int index, InsetPlacement placement)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
friend class QCPGraph
Definition: qcustomplot.h:3846
Q_SLOT void axisSelectableChanged(QCPAxis::SelectableParts selectableParts)
bool isInvalidData(double value)
Definition: qcustomplot.h:312
QPen mainPen() const
void clear()
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE
virtual void updateMapImage()
QSharedPointer< QCPDataContainer< QCPGraphData > > mDataContainer
Definition: qcustomplot.h:3902
virtual int elementCount() const Q_DECL_OVERRIDE
void setTickOrigin(double origin)
void setScatterStyle(const QCPScatterStyle &scatterStyle, QCPScatterStyle::ScatterProperties usedProperties=QCPScatterStyle::spPen)
QCPColorGradient gradient() const
Definition: qcustomplot.h:5068
QCPAxis * rangeZoomAxis(Qt::Orientation orientation)
void setLowerEnding(const QCPLineEnding &ending)
0x08 bottom margin
Definition: qcustomplot.h:207
virtual ~QCPAxisRect()
Open-High-Low-Close bar representation.
Definition: qcustomplot.h:5886
An ellipse is drawn. The size of the ellipse is given by the bracket width/height properties...
Definition: qcustomplot.h:4534
bool clipToAxisRect() const
Definition: qcustomplot.h:3527
bool registerGraph(QCPGraph *graph)
QCPAxisRect * axisRectAt(const QPointF &pos) const
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
QCPItemPosition *const end
Definition: qcustomplot.h:6212
Q_SLOT void replot(QCustomPlot::RefreshPriority refreshPriority=QCustomPlot::rpRefreshHint)
void plottableDoubleClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event)
void updateAxesOffset(QCPAxis::AxisType type)
QCPVector2D & operator+=(const QCPVector2D &vector)
QCPColorMapData * mMapData
Definition: qcustomplot.h:5796
QCPPlottableLegendItem(QCPLegend *parent, QCPAbstractPlottable *plottable)
int plottableCount() const
QCPLayout * mParentLayout
Definition: qcustomplot.h:1263
virtual int calculateAutoMargin(QCP::MarginSide side) Q_DECL_OVERRIDE
double cell(int keyIndex, int valueIndex)
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
QCPAxis * mParentAxis
Definition: qcustomplot.h:1905
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
void dataRangeChanged(const QCPRange &newRange)
Hours (%h in setTimeFormat)
Definition: qcustomplot.h:1655
void setMinimumMargins(const QMargins &margins)
The base class tick generator used by QCPAxis to create tick positions and tick labels.
Definition: qcustomplot.h:1541
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
A filled square.
Definition: qcustomplot.h:1497
QFont mLabelFont
Definition: qcustomplot.h:2147
QCPAxis(QCPAxisRect *parent, AxisType type)
void setScaleRatio(const QCPAxis *otherAxis, double ratio=1.0)
virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE
PainterModes modes() const
Definition: qcustomplot.h:486
QCPRange dataRange() const
Definition: qcustomplot.h:5066
QPoint toPoint() const
Definition: qcustomplot.h:411
void setBrush(const QBrush &brush)
void applyPen(QCPPainter *painter) const
Fractions are written using sub- and superscript UTF-8 digits and the fraction symbol.
Definition: qcustomplot.h:1787
virtual void reallocateBuffer()=0
void setAntialiasedElement(QCP::AntialiasedElement antialiasedElement, bool enabled=true)
void setTickOrigin(double origin)
void setKeySize(int keySize)
The abstract base class for paint buffers, which define the rendering backend.
Definition: qcustomplot.h:523
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
virtual QCPLayoutElement * elementAt(int index) const Q_DECL_OVERRIDE
virtual ~QCPColorScale()
void remove(QCPBars *bars)
QPen mSubGridPen
Definition: qcustomplot.h:1902
QCPGrid * mGrid
Definition: qcustomplot.h:2171
int padding() const
Definition: qcustomplot.h:2043
QCP::AntialiasedElements antialiasedElements() const
Definition: qcustomplot.h:3638
QCPDataSelection selection() const
Definition: qcustomplot.h:3324
void setMaximumSize(const QSize &size)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
Color channels hue, saturation and value are linearly interpolated (The hue is interpolated over the ...
Definition: qcustomplot.h:4448
void setDevicePixelRatio(double ratio)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
QString text() const
Definition: qcustomplot.h:4954
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
Holds the data of one single data point for QCPCurve.
Definition: qcustomplot.h:5268
void mouseMove(QMouseEvent *event)
QCPRange keyRange() const
Definition: qcustomplot.h:5699
No line is drawn between data points (e.g. only scatters)
Definition: qcustomplot.h:5314
QCustomPlot(QWidget *parent=0)
QList< QCPDataRange > dataRanges() const
Definition: qcustomplot.h:960
void setParentLayerable(QCPLayerable *parentLayerable)
QCPItemBracket(QCustomPlot *parentPlot)
QCPLineEnding tail() const
Definition: qcustomplot.h:6198
void rescaleKeyAxis(bool onlyEnlarge=false) const
void addData(const QVector< double > &keys, const QVector< double > &values, bool alreadySorted=false)
bool mNumberBeautifulPowers
Definition: qcustomplot.h:2157
QCPItemAnchor *const center
Definition: qcustomplot.h:6426
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE
A legend item representing a plottable with an icon and the plottable name.
Definition: qcustomplot.h:4780
QHash< QCP::MarginSide, QCPMarginGroup * > mMarginGroups
Definition: qcustomplot.h:1269
Bar spacing is given by a fraction of the axis rect size.
Definition: qcustomplot.h:5398
bool antialiasing() const
Definition: qcustomplot.h:485
virtual double dataMainValue(int index) const =0
void setType(QCPAxis::AxisType type)
void setIconTextPadding(int padding)
each data point is represented by a line parallel to the value axis, which reaches from the data poin...
Definition: qcustomplot.h:5182
QCPItemPosition *const start
Definition: qcustomplot.h:6209
void selectionChanged(bool selected)
int numberPrecision() const
Definition: qcustomplot.h:2028
virtual QList< QCPLayoutElement * > elements(bool recursive) const
void expandTo(int newRowCount, int newColumnCount)
SelectableParts selectedParts() const
virtual void reallocateBuffer() Q_DECL_OVERRIDE
virtual void drawFill(QCPPainter *painter, QVector< QPointF > *lines) const
QByteArray mLabelParameterHash
Definition: qcustomplot.h:2277
0x01 Mode for vectorized painting (e.g. PDF export). For example, this prevents some antialiasing fix...
Definition: qcustomplot.h:473
void applyBrush(QCPPainter *painter) const
QBrush mSelectedBrush
Definition: qcustomplot.h:6583
void setMedianPen(const QPen &pen)
QCPRange bounded(double lowerBound, double upperBound) const
QList< QCPAbstractPlottable * > selectedPlottables() const
QBrush mBackgroundBrush
Definition: qcustomplot.h:4671
void selectionChanged(bool selected)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
Q_SLOT bool setLayer(QCPLayer *layer)
QList< QCPGraph * > graphs() const
QCPAbstractPlottable * plottable()
Definition: qcustomplot.h:4787
bool contains(double value) const
Definition: qcustomplot.h:812
bool registerPlottable(QCPAbstractPlottable *plottable)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
The axis backbone and tick marks.
Definition: qcustomplot.h:2002
void setTickStepStrategy(TickStepStrategy strategy)
Fractions are displayed as regular decimal floating point numbers, e.g. "0.25" or "0...
Definition: qcustomplot.h:1785
double realLength() const
bool scaled() const
Definition: qcustomplot.h:6467
virtual ~QCPGraph()
QCPLineEnding lowerEnding
Definition: qcustomplot.h:2241
static double dateTimeToKey(const QDateTime dateTime)
The errors are for the key dimension (bars appear parallel to the key axis)
Definition: qcustomplot.h:6010
EndingStyle style() const
Definition: qcustomplot.h:1509
Bar spacing is in key coordinates and thus scales with the key axis range.
Definition: qcustomplot.h:5399
QCPItemAnchor * anchor(const QString &name) const
void drawGridLines(QCPPainter *painter) const
void setSpacingType(SpacingType spacingType)
SelectionType
Definition: qcustomplot.h:299
void setSelectedTickLabelFont(const QFont &font)
void setOffset(int offset)
QColor mSelectedTickLabelColor
Definition: qcustomplot.h:2154
virtual void wheelEvent(QWheelEvent *event)
void setBasePen(const QPen &pen)
bool hasPlottable(QCPAbstractPlottable *plottable) const
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
virtual ~QCPItemText()
void addData(const QVector< double > &t, const QVector< double > &keys, const QVector< double > &values, bool alreadySorted=false)
void setVisible(bool visible)
The tracer is not visible.
Definition: qcustomplot.h:6539
Controls how a plottable&#39;s data selection is drawn.
Definition: qcustomplot.h:3248
void simplifyFraction(int &numerator, int &denominator) const
QString text() const
Definition: qcustomplot.h:6322
int end() const
Definition: qcustomplot.h:909
void setSelectedBorderPen(const QPen &pen)
void zoom(const QRectF &pixelRect)
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
bool isActive() const
Definition: qcustomplot.h:1114
int bottom() const
Definition: qcustomplot.h:4655
void setErrorType(ErrorType type)
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE
void setPixmap(const QPixmap &pixmap)
QColor getTickLabelColor() const
virtual void updateLayout() Q_DECL_OVERRIDE
QCPItemPosition *const topLeft
Definition: qcustomplot.h:6260
SelectableParts mSelectedParts
Definition: qcustomplot.h:2141
QList< QCPLayerable * > layerableListAt(const QPointF &pos, bool onlySelectable, QList< QVariant > *selectionDetails=0) const
custom painter operations are performed per scatter (As QPainterPath, see setCustomPath) ...
Definition: qcustomplot.h:2343
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
The abstract base class for all entries in a QCPLegend.
Definition: qcustomplot.h:4716
0x08 The shape property, see setShape
Definition: qcustomplot.h:2312
void getVisibleDataBounds(QCPErrorBarsDataContainer::const_iterator &begin, QCPErrorBarsDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
bool hasItemWithPlottable(const QCPAbstractPlottable *plottable) const
const QPolygonF getChannelFillPolygon(const QVector< QPointF > *lineData, QCPDataRange thisSegment, const QVector< QPointF > *otherData, QCPDataRange otherSegment) const
QFont mSelectedTickLabelFont
Definition: qcustomplot.h:2153
QColor mColor
Definition: qcustomplot.h:6360
void setHead(const QCPLineEnding &head)
double ohlcSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const
bool remove(QCPLayoutElement *element)
{ssPeace.png} a circle, with one vertical and two downward diagonal lines
Definition: qcustomplot.h:2341
QBrush mBrush
Definition: qcustomplot.h:6274
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE
bool twoColored() const
Definition: qcustomplot.h:5899
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
Queues the entire replot for the next event loop iteration. This way multiple redundant replots can b...
Definition: qcustomplot.h:3624
Tick labels (numbers) of this axis (as a whole, not individually)
Definition: qcustomplot.h:2003
bool mAntialiasedZeroLine
Definition: qcustomplot.h:1901
void setStyle(BracketStyle style)
virtual void draw(QCPPainter *painter)
void setSubTickCount(int subTicks)
void itemDoubleClick(QCPAbstractItem *item, QMouseEvent *event)
QString fractionToString(int numerator, int denominator) const
An integer multiple of the specified tick step is allowed. The used factor follows the base class pro...
Definition: qcustomplot.h:1706
QColor getTextColor() const
void getVisibleDataBounds(QCPBarsDataContainer::const_iterator &begin, QCPBarsDataContainer::const_iterator &end) const
QColor mSelectedTextColor
Definition: qcustomplot.h:4992
double dot(const QCPVector2D &vec) const
Definition: qcustomplot.h:418
void addChild(QCPLayerable *layerable, bool prepend)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setPenPositive(const QPen &pen)
QCPAxisRect * mAxisRect
Definition: qcustomplot.h:2137
static AxisType marginSideToAxisType(QCP::MarginSide side)
0x0001 Axis base line and tick marks
Definition: qcustomplot.h:222
QCPLineEnding mHead
Definition: qcustomplot.h:6165
QCPItemAnchor * createAnchor(const QString &name, int anchorId)
Q_SLOT void setDataRange(const QCPRange &dataRange)
void setScatterSkip(int skip)
void setName(const QString &name)
void normalize()
QCPAxis::AxisType mType
Definition: qcustomplot.h:5098
QPainterPath customPath() const
Definition: qcustomplot.h:2361
void setRowStretchFactors(const QList< double > &factors)
{ssSquare.png} a square
Definition: qcustomplot.h:2332
void setRangeReversed(bool reversed)
QPointF getOptimizedPoint(int prevRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
QCP::SelectionType selectable() const
Definition: qcustomplot.h:3322
void setStackingGap(double pixels)
void setSelectedBasePen(const QPen &pen)
0x0008 Legend box
Definition: qcustomplot.h:225
virtual QCPRange dataValueRange(int index) const Q_DECL_OVERRIDE
double getTangentAngle(const QCPPlottableInterface1D *interface1d, int dataIndex, int direction) const
Responsible for drawing the grid of a QCPAxis.
Definition: qcustomplot.h:1868
Qt::Alignment positionAlignment() const
Definition: qcustomplot.h:6323
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
0x001 Axis ranges are draggable (see QCPAxisRect::setRangeDrag, QCPAxisRect::setRangeDragAxes) ...
Definition: qcustomplot.h:259
QCPItemAnchor * parentAnchor() const
Definition: qcustomplot.h:3471
void setTangentToData(bool enabled)
0x00 no margin
Definition: qcustomplot.h:209
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE
QVector< QString > tickLabels
Definition: qcustomplot.h:2262
void getTraverseCornerPoints(int prevRegion, int currentRegion, double keyMin, double valueMax, double keyMax, double valueMin, QVector< QPointF > &beforeTraverse, QVector< QPointF > &afterTraverse) const
QImage mMapImage
Definition: qcustomplot.h:5803
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
QSize maximumSize() const
Definition: qcustomplot.h:1235
void setWhiskerWidth(double width)
void setPen(const QPen &pen)
void changed(const QRect &rect, QMouseEvent *event)
QCPLayer * mLayer
Definition: qcustomplot.h:741
void setTextAlignment(Qt::Alignment alignment)
bool visible() const
Definition: qcustomplot.h:715
void gradientChanged(const QCPColorGradient &newGradient)
const QCPRange range() const
Definition: qcustomplot.h:2017
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
virtual ~QCPItemLine()
void dataRangeChanged(const QCPRange &newRange)
width is in key coordinates and thus scales with the key axis range
Definition: qcustomplot.h:5877
QCPColorGradient mGradient
Definition: qcustomplot.h:5101
void setTicker(QSharedPointer< QCPAxisTicker > ticker)
int itemCount() const
0x04 top margin
Definition: qcustomplot.h:206
QList< QCPGraph * > graphs() const
Defines an abstract interface for one-dimensional plottables.
Definition: qcustomplot.h:3858
void setSelectedSubTickPen(const QPen &pen)
void legendDoubleClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
virtual ~QCPAxis()
int dataPointCount() const
QFont font() const
Definition: qcustomplot.h:4956
ErrorType mErrorType
Definition: qcustomplot.h:6059
QSharedPointer< QCPStatisticalBoxDataContainer > data() const
Definition: qcustomplot.h:5626
The selection rect is disabled, and all mouse events are forwarded to the underlying objects...
Definition: qcustomplot.h:275
QCPColorMapData * data() const
Definition: qcustomplot.h:5761
double size() const
Definition: qcustomplot.h:2356
bool noAntialiasingOnDrag() const
Definition: qcustomplot.h:3643
QRect axisSelectionBox() const
Definition: qcustomplot.h:2234
Provides rect/rubber-band data selection and range zoom interaction.
Definition: qcustomplot.h:1102
void setCustomPath(const QPainterPath &customPath)
void getCurveLines(QVector< QPointF > *lines, const QCPDataRange &dataRange, double penWidth) const
void setBegin(int begin)
Definition: qcustomplot.h:914
virtual ~QCPColorMap()
Linear scaling.
Definition: qcustomplot.h:1993
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
Fractions are written as rationals using ASCII characters only, e.g. "1/4" or "1/8".
Definition: qcustomplot.h:1786
QColor color() const
Definition: qcustomplot.h:6314
double rectDistance(const QRectF &rect, const QPointF &pos, bool filledRect) const
QList< QCPItemPosition * > mPositions
Definition: qcustomplot.h:3556
QList< QCPDataRange > mDataRanges
Definition: qcustomplot.h:976
virtual void drawBracket(QCPPainter *painter, int direction) const
void setShape(ScatterShape shape)
QCP::PlottingHints plottingHints() const
Definition: qcustomplot.h:3644
QCustomPlot * mParentPlot
Definition: qcustomplot.h:3421
void makeNonCosmetic()
QCPLayer * layer() const
Definition: qcustomplot.h:718
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setBackgroundScaled(bool scaled)
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
Colors suitable for thermal imaging, ranging from dark blue over purple to orange, yellow and white.
Definition: qcustomplot.h:4463
void drawOhlcPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected)
void setTickLabels(bool show)
An anchor of an item to which positions can be attached to.
Definition: qcustomplot.h:3405
bool addToLegend(QCPLegend *legend)
ScaleStrategy mScaleStrategy
Definition: qcustomplot.h:1724
void setSelectedPen(const QPen &pen)
Qt::Orientations mRangeZoom
Definition: qcustomplot.h:4677
void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
void enforceType(QCP::SelectionType type)
void setBrush(const QBrush &brush)
void setData(QSharedPointer< QCPErrorBarsDataContainer > data)
void setWrap(int count)
int mSelectionTolerance
Definition: qcustomplot.h:3774
QBrush brush() const
Definition: qcustomplot.h:1113
void getVisibleDataBounds(QCPGraphDataContainer::const_iterator &begin, QCPGraphDataContainer::const_iterator &end, const QCPDataRange &rangeRestriction) const
QBrush mainBrush() const
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:3280
virtual QList< QCPLayoutElement * > elements(bool recursive) const Q_DECL_OVERRIDE
void axisDoubleClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
QCPAxis * keyAxis() const
Definition: qcustomplot.h:3477
Phase in which the margins are calculated and set.
Definition: qcustomplot.h:1206
QPen mainPen() const
Q_SLOT void setSelectableParts(const QCPAxis::SelectableParts &selectableParts)
int labelPadding() const
void setSelectedIconBorderPen(const QPen &pen)
Static positioning in pixels, starting from the top left corner of the viewport/widget.
Definition: qcustomplot.h:3453
void setAntialiasedScatters(bool enabled)
double width() const
Definition: qcustomplot.h:5627
void setRangeZoom(Qt::Orientations orientations)
virtual QRect clipRect() const Q_DECL_OVERRIDE
SelectionRectMode
Definition: qcustomplot.h:275
QCPItemRect(QCustomPlot *parentPlot)
QCPItemAnchor * mParentAnchorX
Definition: qcustomplot.h:3501
void indexToRowCol(int index, int &row, int &column) const
0x0004 Sub grid lines
Definition: qcustomplot.h:224
QCache< QString, CachedLabel > mLabelCache
Definition: qcustomplot.h:2278
void mouseRelease(QMouseEvent *event)
A brace with angled edges.
Definition: qcustomplot.h:6621
void setSelectedPen(const QPen &pen)
QCPItemPosition *const right
Definition: qcustomplot.h:6647
void setWhiskerPen(const QPen &pen)
void setAdaptiveSampling(bool enabled)
int findIndexAboveY(const QVector< QPointF > *data, double y) const
void getScatters(QVector< QPointF > *scatters, const QCPDataRange &dataRange, double scatterWidth) const
QVector< double > subTickPositions
Definition: qcustomplot.h:2260
void rangeChanged(const QCPRange &newRange)
void setSelectionDecorator(QCPSelectionDecorator *decorator)
virtual QList< QCPLayoutElement * > elements(bool recursive) const Q_DECL_OVERRIDE
A plus shaped crosshair with limited size.
Definition: qcustomplot.h:6540
void rescaleDataRange(bool onlyVisibleMaps)
void mouseDoubleClick(QMouseEvent *event)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setupFullAxesBox(bool connectRanges=false)
virtual ~QCustomPlot()
void setText(const QString &text)
QCPItemLine(QCustomPlot *parentPlot)
void setRangeZoomAxes(QCPAxis *horizontal, QCPAxis *vertical)
Q_SLOT void setSelectableParts(const SelectableParts &selectableParts)
QCPLineEnding mTail
Definition: qcustomplot.h:6217
QCPAxis * addAxis(QCPAxis::AxisType type, QCPAxis *axis=0)
double tickLabelRotation() const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
0x0002 Grid lines
Definition: qcustomplot.h:223
QColor mainColor() const
QCPLineEnding head() const
Definition: qcustomplot.h:6197
0x01 left margin
Definition: qcustomplot.h:204
void setFromOther(const QCPScatterStyle &other, ScatterProperties properties)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
static bool validRange(double lower, double upper)
Base class for all drawable objects.
Definition: qcustomplot.h:700
QBrush mBrushNegative
Definition: qcustomplot.h:5936
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:3777
void setInterpolating(bool enabled)
void setValueAxis(QCPAxis *axis)
bool segmentsIntersect(double aLower, double aUpper, double bLower, double bUpper, int &bPrecedence) const
QSharedPointer< QCPAxisTicker > ticker() const
Definition: qcustomplot.h:2019
double distanceSquaredToLine(const QCPVector2D &start, const QCPVector2D &end) const
void setBackground(const QPixmap &pm)
double getPixelSpacing(const QCPBars *bars, double keyCoord)
void setPadding(int padding)
QCPAxis * xAxis
Definition: qcustomplot.h:3739
bool removeFromLegend(QCPLegend *legend) const
virtual Q_SLOT void processRectSelection(QRect rect, QMouseEvent *event)
A plottable representing a bar chart in a plot.
Definition: qcustomplot.h:5479
const QCP::Interactions interactions() const
Definition: qcustomplot.h:3641
virtual QCPPlottableInterface1D * interface1D()
Definition: qcustomplot.h:3341
QPen subTickPen() const
Definition: qcustomplot.h:2038
virtual int findEnd(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE
QFont font() const
Definition: qcustomplot.h:6320
bool removeGraph(QCPGraph *graph)
QVector< double > mTickVector
Definition: qcustomplot.h:2174
void setRange(const QCPRange &keyRange, const QCPRange &valueRange)
void setBrush(const QBrush &brush)
0x080 All other objects are selectable (e.g. your own derived layerables, other layout elements...
Definition: qcustomplot.h:266
QList< QCPAbstractPlottable * > plottables() const
virtual void layoutChanged() Q_DECL_OVERRIDE
double mStackingGap
Definition: qcustomplot.h:5545
void setCoords(double key, double value)
void addChild(QCP::MarginSide side, QCPLayoutElement *element)
void colorize(const double *data, const QCPRange &range, QRgb *scanLine, int n, int dataIndexFactor=1, bool logarithmic=false)
SpacingType mSpacingType
Definition: qcustomplot.h:5428
0x0080 Scatter symbols of plottables (excluding scatter symbols of type ssPixmap) ...
Definition: qcustomplot.h:229
friend class QCPItemAnchor
Definition: qcustomplot.h:3581
Q_SLOT void setSelectable(bool selectable)
void setWidth(double width)
void loadPreset(GradientPreset preset)
void moveAbove(QCPBars *bars)
double mSymbolGap
Definition: qcustomplot.h:6061
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void setSize(int keySize, int valueSize)
void moveBelow(QCPBars *bars)
QSharedPointer< QCPFinancialDataContainer > data() const
Definition: qcustomplot.h:5895
void setScatterStyle(const QCPScatterStyle &style)
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
void setInteraction(const QCP::Interaction &interaction, bool enabled=true)
virtual void simplify() Q_DECL_OVERRIDE
void setRangeUpper(double upper)
virtual ~QCPMarginGroup()
void setModes(PainterModes modes)
virtual QPointF pixelPosition() const
void setData(QSharedPointer< QCPBarsDataContainer > data)
QCPItemTracer(QCustomPlot *parentPlot)
QPen mSubTickPen
Definition: qcustomplot.h:2164
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
void recalculateDataBounds()
double getPixelWidth(double key, double keyPixel) const
int valueSize() const
Definition: qcustomplot.h:5698
bool invalidated() const
Definition: qcustomplot.h:531
bool contains(const QCPDataSelection &other) const
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
virtual QRect clipRect() const
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
bool mScaledPixmapInvalidated
Definition: qcustomplot.h:6498
virtual QCP::Interaction selectionCategory() const
void trimTicks(const QCPRange &range, QVector< double > &ticks, bool keepOneOutlier) const
void layerChanged(QCPLayer *newLayer)
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
bool addElement(int row, int column, QCPLayoutElement *element)
QPen pen() const
Definition: qcustomplot.h:6195
void setStyle(EndingStyle style)
QPointer< QCPBars > mBarBelow
Definition: qcustomplot.h:5546
QPixmap mScaledPixmap
Definition: qcustomplot.h:6496
void setValueSize(int valueSize)
QPointF getFillBasePoint(QPointF matchingDataPoint) const
void addData(const QVector< double > &keys, const QVector< double > &minimum, const QVector< double > &lowerQuartile, const QVector< double > &median, const QVector< double > &upperQuartile, const QVector< double > &maximum, bool alreadySorted=false)
virtual ~QCPLayoutElement()
QList< QCPRange > mDragStartHorzRange
Definition: qcustomplot.h:4683
QBrush mBrush
Definition: qcustomplot.h:6362
QList< QCPLayerable * > children() const
Definition: qcustomplot.h:663
void setTickLabelRotation(double degrees)
double spacing() const
Definition: qcustomplot.h:5408
QRect mTextBoundingRect
Definition: qcustomplot.h:4993
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:5349
void setTypeX(PositionType type)
virtual ~QCPItemTracer()
TracerStyle mStyle
Definition: qcustomplot.h:6585
0x02 Mode for all sorts of exports (e.g. PNG, PDF,...). For example, this prevents using cached pixma...
Definition: qcustomplot.h:474
QCPBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
A layout that arranges child elements in a grid.
Definition: qcustomplot.h:1330
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
QCPVector2D & operator-=(const QCPVector2D &vector)
void setPen(const QPen &pen)
int pixelOrientation() const
Definition: qcustomplot.h:2109
virtual ~QCPBars()
void setFont(const QFont &font)
int levelCount() const
Definition: qcustomplot.h:4477
void setColor(const QColor &color)
bool registerItem(QCPAbstractItem *item)
void selectionChangedByUser()
virtual bool take(QCPLayoutElement *element) Q_DECL_OVERRIDE
QMargins margins() const
Definition: qcustomplot.h:1231
Minimum/Maximum size constraints apply to inner rect.
Definition: qcustomplot.h:1219
0x08 Axis is horizontal and on the bottom side of the axis rect
Definition: qcustomplot.h:1975
QPointer< QCPAbstractPlottable > mDataPlottable
Definition: qcustomplot.h:6058
QPen mBasePen
Definition: qcustomplot.h:2142
double mBufferDevicePixelRatio
Definition: qcustomplot.h:3765
QPointer< QCPBars > mBarAbove
Definition: qcustomplot.h:5546
QCPScatterStyle mOutlierStyle
Definition: qcustomplot.h:5663
bool rangeDrag() const
void setUpperEnding(const QCPLineEnding &ending)
void setPixmap(const QPixmap &pixmap)
void setWidth(double width)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
double mSpacing
Definition: qcustomplot.h:5429
Multiple contiguous data points (a data range) can be selected.
Definition: qcustomplot.h:302
QCPItemText(QCustomPlot *parentPlot)
virtual bool registerWithPlottable(QCPAbstractPlottable *plottable)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
void setUsedScatterProperties(const QCPScatterStyle::ScatterProperties &properties)
QList< QCPLayer * > mLayers
Definition: qcustomplot.h:3771
Seconds (%s in setTimeFormat)
Definition: qcustomplot.h:1653
QBrush mainBrush() const
void setRangeDrag(Qt::Orientations orientations)
void setTangentAverage(int pointCount)
QCP::AntialiasedElements mNotAntialiasedElements
Definition: qcustomplot.h:3772
virtual void generate(const QCPRange &range, const QLocale &locale, QChar formatChar, int precision, QVector< double > &ticks, QVector< double > *subTicks, QVector< QString > *tickLabels)
TimeUnit mSmallestUnit
Definition: qcustomplot.h:1676
double mTickOrigin
Definition: qcustomplot.h:1577
QCPItemAnchor *const bottom
Definition: qcustomplot.h:6352
void setSize(const QSize &size)
QCPDataRange expanded(const QCPDataRange &other) const
bool mDragging
Definition: qcustomplot.h:2179
double y() const
Definition: qcustomplot.h:400
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
0x0040 Main lines of items
Definition: qcustomplot.h:228
void removeChild(QCPLayerable *layerable)
QCPAxisRect * axisRect() const
QImage mUndersampledMapImage
Definition: qcustomplot.h:5803
Represents two doubles as a mathematical 2D vector.
Definition: qcustomplot.h:390
QCPVector2D perpendicular() const
Definition: qcustomplot.h:417
Holds the data of one single data point for QCPGraph.
Definition: qcustomplot.h:5129
QCPDataSelection & operator-=(const QCPDataSelection &other)
no scatter symbols are drawn (e.g. in QCPGraph, data only represented with lines) ...
Definition: qcustomplot.h:2326
TimeUnit mBiggestUnit
Definition: qcustomplot.h:1676
bool mMouseHasMoved
Definition: qcustomplot.h:3791
bool begin(QPaintDevice *device)
void setSelectedTextColor(const QColor &color)
virtual void drawTickLabel(QCPPainter *painter, double x, double y, const TickLabelData &labelData) const
double value() const
Definition: qcustomplot.h:3475
virtual void placeTickLabel(QCPPainter *painter, double position, int distanceToAxis, const QString &text, QSize *tickLabelsSize)
void updateScaledPixmap(QRect finalRect=QRect(), bool flipHorz=false, bool flipVert=false)
double getMantissa(double input, double *magnitude=0) const
QCPLineEnding upperEnding() const
The abstract base class for all data representing objects in a plot.
Definition: qcustomplot.h:3295
QCP::MarginSides mAutoMargins
Definition: qcustomplot.h:1268
FractionStyle mFractionStyle
Definition: qcustomplot.h:1810
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void selectionChanged(bool selected)
void setColorScale(QCPColorScale *colorScale)
void fill(double z)
bool saveRastered(const QString &fileName, int width, int height, double scale, const char *format, int quality=-1, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch)
EndingStyle mStyle
Definition: qcustomplot.h:1528
QCPScatterStyle outlierStyle() const
Definition: qcustomplot.h:5633
QCPColorGradient inverted() const
Hue variation similar to a spectrum, often used in numerical visualization (creates banding illusion ...
Definition: qcustomplot.h:4466
QRectF insetRect(int index) const
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void getScatters(QVector< QPointF > *scatters, const QCPDataRange &dataRange) const
QCPRange sanitizedForLogScale() const
void setSelectedBrush(const QBrush &brush)
void setBackground(const QPixmap &pm)
virtual QByteArray generateLabelParameterHash() const
QPointF coords() const
Definition: qcustomplot.h:3476
void releaseElement(QCPLayoutElement *el)
ExportPen
Definition: qcustomplot.h:183
void removeChildX(QCPItemPosition *pos)
QCPItemAnchor *const left
Definition: qcustomplot.h:6425
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:3369
void setSubTickLengthOut(int outside)
bool removeAt(int index)
double mGraphKey
Definition: qcustomplot.h:6587
virtual QSize sizeHint() const Q_DECL_OVERRIDE
void setAxes(QCPAxis *keyAxis, QCPAxis *valueAxis)
double mWhiskerWidth
Definition: qcustomplot.h:6060
bool subTicks() const
Definition: qcustomplot.h:2033
void setDateTimeFormat(const QString &format)
QList< QCPBars * > bars() const
Definition: qcustomplot.h:5415
QCPDataRange adjusted(int changeBegin, int changeEnd) const
Definition: qcustomplot.h:923
QList< QPointer< QCPAxis > > mRangeDragVertAxis
Definition: qcustomplot.h:4678
virtual void drawImpulsePlot(QCPPainter *painter, const QVector< QPointF > &lines) const
void setTwoColored(bool twoColored)
virtual ~QCPCurve()
QVector< QPointF > dataToStepRightLines(const QVector< QCPGraphData > &data) const
virtual void parentPlotInitialized(QCustomPlot *parentPlot) Q_DECL_OVERRIDE
bool moveToLayer(QCPLayer *layer, bool prepend)
QPointer< QCPAxis > mColorAxis
Definition: qcustomplot.h:5106
0x008 Plottables are selectable (e.g. graphs, curves, bars,... see QCPAbstractPlottable) ...
Definition: qcustomplot.h:262
bool saveJpg(const QString &fileName, int width=0, int height=0, double scale=1.0, int quality=-1, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch)
virtual ~QCPLayer()
void setSubGridPen(const QPen &pen)
QList< QCPAbstractLegendItem * > selectedItems() const
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE
QVector< QCPDataRange > getNonNanSegments(const QVector< QPointF > *lineData, Qt::Orientation keyOrientation) const
void setAntialiasedSubGrid(bool enabled)
A filled circle.
Definition: qcustomplot.h:1496
Q_SLOT void setSelectable(QCP::SelectionType selectable)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const =0
virtual ~QCPItemStraightLine()
Tick labels will be displayed outside the axis rect.
Definition: qcustomplot.h:1986
QCPItemAnchor * parentAnchorY() const
Definition: qcustomplot.h:3473
Continuous lightness from black over icey colors to white (suited for non-biased data representation)...
Definition: qcustomplot.h:4458
void scaleRange(double factor)
line is drawn as steps where the step height is the value of the left data point
Definition: qcustomplot.h:5179
virtual int dataCount() const Q_DECL_OVERRIDE
void selectionChanged(const QCPAxis::SelectableParts &parts)
virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE
void plottableClick(QCPAbstractPlottable *plottable, int dataIndex, QMouseEvent *event)
virtual TickLabelData getTickLabelData(const QFont &font, const QString &text) const
QCPItemPosition *const endDir
Definition: qcustomplot.h:6211
QPen getBasePen() const
Columns are filled first, and a new element is wrapped to the next row if the column count would exce...
Definition: qcustomplot.h:1353
QCPVector2D normalized() const
0x02 Axis is vertical and on the right side of the axis rect
Definition: qcustomplot.h:1973
An approximation of the visible light spectrum (creates banding illusion but allows more precise magn...
Definition: qcustomplot.h:4465
void setBackgroundScaled(bool scaled)
void setTickLabelFont(const QFont &font)
SignDomain
Definition: qcustomplot.h:194
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:6417
QList< QPointer< QCPAxis > > mRangeDragHorzAxis
Definition: qcustomplot.h:4678
QCPColorMapData(int keySize, int valueSize, const QCPRange &keyRange, const QCPRange &valueRange)
double width() const
Definition: qcustomplot.h:1510
void setFieldWidth(TimeUnit unit, int width)
bool mInterpolate
Definition: qcustomplot.h:5798
int subTickLengthIn() const
QCPColorGradient mGradient
Definition: qcustomplot.h:5797
void setTextColor(const QColor &color)
virtual ~QCPItemEllipse()
QList< QPointer< QCPAxis > > mRangeZoomVertAxis
Definition: qcustomplot.h:4679
Bar width is in absolute pixels.
Definition: qcustomplot.h:5498
int tickLengthOut() const
Qt::AspectRatioMode aspectRatioMode() const
Definition: qcustomplot.h:6468
void setSelectionRectMode(QCP::SelectionRectMode mode)
QList< QCPAxis * > axes() const
QCustomPlot * mParentPlot
Definition: qcustomplot.h:676
bool removeLayer(QCPLayer *layer)
void setSelectedLabelFont(const QFont &font)
virtual int size() const
bool getTraverse(double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin, QPointF &crossA, QPointF &crossB) const
void setPiValue(double pi)
void getPixelWidth(double key, double &lower, double &upper) const
QCPColorScale * colorScale() const
Definition: qcustomplot.h:5767
A plottable representing a graph in a plot.
Definition: qcustomplot.h:5160
QCPAbstractPlottable * mPlottable
Definition: qcustomplot.h:3283
void applyFillAntialiasingHint(QCPPainter *painter) const
const QPolygonF getFillPolygon(const QVector< QPointF > *lineData, QCPDataRange segment) const
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
QCPItemPosition *const start
Definition: qcustomplot.h:6159
int iconTextPadding() const
Definition: qcustomplot.h:4846
The abstract base class for all items in a plot.
Definition: qcustomplot.h:3513
QCPAbstractLegendItem(QCPLegend *parent)
QString name() const
Definition: qcustomplot.h:661
QPen mainPen() const
QCPLayoutInset * mInsetLayout
Definition: qcustomplot.h:4676
void setData(double key, double value, double z)
virtual QCPLayoutElement * takeAt(int index) Q_DECL_OVERRIDE
double length() const
Definition: qcustomplot.h:6634
QVector< QPointF > dataToStepCenterLines(const QVector< QCPGraphData > &data) const
Q_SLOT void setSelectable(bool selectable)
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
Q_SLOT void setGradient(const QCPColorGradient &gradient)
0x004 axis (tick) labels will be cached as pixmaps, increasing replot performance.
Definition: qcustomplot.h:248
QRect tickLabelsSelectionBox() const
Definition: qcustomplot.h:2235
void setPadding(const QMargins &padding)
Logarithmic scaling with correspondingly transformed axis coordinates (possibly also setTicker to a Q...
Definition: qcustomplot.h:1994
Modifications are not allowed, the specified tick step is absolutely fixed. This might cause a high t...
Definition: qcustomplot.h:1705
QCPLegend * mParentLegend
Definition: qcustomplot.h:4757
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:3369
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const =0
QBrush brush() const
Definition: qcustomplot.h:6553
QVector< int > getSectionSizes(QVector< int > maxSizes, QVector< int > minSizes, QVector< double > stretchFactors, int totalSize) const
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
void setRangeLower(double lower)
void setBrush(const QBrush &brush)
QCPItemCurve(QCustomPlot *parentPlot)
QCPLineEnding mHead
Definition: qcustomplot.h:6217
void setTickCount(int count)
QFont mSelectedLabelFont
Definition: qcustomplot.h:2147
QCPColorScale(QCustomPlot *parentPlot)
virtual void moveSelection(QMouseEvent *event)
QCPItemAnchor *const right
Definition: qcustomplot.h:6264
bool mNoAntialiasingOnDrag
Definition: qcustomplot.h:3775
QCPAbstractPaintBuffer * createPaintBuffer()
The central class of the library. This is the QWidget which displays the plot and interacts with the ...
Definition: qcustomplot.h:3590
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
virtual ~QCPPaintBufferPixmap()
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE
void setAntialiasing(bool enabled)
void setPen(const QPen &pen)
0x040 Items are selectable (Rectangles, Arrows, Textitems, etc. see QCPAbstractItem) ...
Definition: qcustomplot.h:265
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
int mScatterSkip
Definition: qcustomplot.h:5350
Candlestick representation.
Definition: qcustomplot.h:5887
void setWidth(double width)
bool saveBmp(const QString &fileName, int width=0, int height=0, double scale=1.0, int resolution=96, QCP::ResolutionUnit resolutionUnit=QCP::ruDotsPerInch)
int rowColToIndex(int row, int column) const
void getMinimumRowColSizes(QVector< int > *minColWidths, QVector< int > *minRowHeights) const
bool selectable() const
Definition: qcustomplot.h:3529
void setBrushNegative(const QBrush &brush)
TickStepStrategy mTickStepStrategy
Definition: qcustomplot.h:1575
Manages a single axis inside a QCustomPlot.
Definition: qcustomplot.h:1919
QCPLineEnding tail() const
Definition: qcustomplot.h:6148
bool isEmpty() const
Definition: qcustomplot.h:966
QCPAxis::AxisType type() const
Definition: qcustomplot.h:5065
bool mSubTicks
Definition: qcustomplot.h:2161
QCPBarsGroup * barsGroup() const
Definition: qcustomplot.h:5510
virtual void mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE
virtual ~QCPLegend()
bool isPenDefined() const
Definition: qcustomplot.h:2374
bool setupOpenGl()
void drawLine(const QLineF &line)
QList< QCPAbstractItem * > selectedItems() const
QBrush brush() const
Definition: qcustomplot.h:2359
double candlestickSelectTest(const QPointF &pos, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, QCPFinancialDataContainer::const_iterator &closestDataPoint) const
Half hue spectrum from black over purple to blue and finally green (creates banding illusion but allo...
Definition: qcustomplot.h:4462
double width() const
Definition: qcustomplot.h:5508
bool mIsAntialiasing
Definition: qcustomplot.h:509
void setRangeDragAxes(QCPAxis *horizontal, QCPAxis *vertical)
virtual int commonMargin(QCP::MarginSide side) const
void setTickPen(const QPen &pen)
Replots immediately, but queues the widget repaint, by calling QWidget::update() after the replot...
Definition: qcustomplot.h:3622
void applyAntialiasingHint(QCPPainter *painter, bool localAntialiased, QCP::AntialiasedElement overrideElement) const
QHash< TimeUnit, QString > mFormatPattern
Definition: qcustomplot.h:1677
QCPColorScale * mParentColorScale
Definition: qcustomplot.h:5028
void setPen(const QPen &pen)
void setColorStops(const QMap< double, QColor > &colorStops)
QCPLayer * layer(const QString &name) const
QPen pen() const
Definition: qcustomplot.h:6145
A filled arrow head with a straight/flat back (a triangle)
Definition: qcustomplot.h:1493
PositionType type() const
Definition: qcustomplot.h:3468
virtual bool take(QCPLayoutElement *element) Q_DECL_OVERRIDE
int findIndexAboveX(const QVector< QPointF > *data, double x) const
Represents the visual appearance of scatter points.
Definition: qcustomplot.h:2296
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
{ssCircle.png} a circle
Definition: qcustomplot.h:2330
virtual QSize maximumOuterSizeHint() const Q_DECL_OVERRIDE
int index() const
Definition: qcustomplot.h:662
virtual QPointF getTickLabelDrawOffset(const TickLabelData &labelData) const
void setOutlierStyle(const QCPScatterStyle &style)
bool isEmpty() const
Definition: qcustomplot.h:919
Manages a legend inside a QCustomPlot.
Definition: qcustomplot.h:4804
Layer is used only for rendering order, and shares paint buffer with all other adjacent logical layer...
Definition: qcustomplot.h:651
QList< QCPLayerable * > mChildren
Definition: qcustomplot.h:679
QSet< QCPItemPosition * > mChildrenY
Definition: qcustomplot.h:3424
void insert(int i, QCPBars *bars)
void setSelectionRect(QCPSelectionRect *selectionRect)
QCPLineEnding lowerEnding() const
QSharedPointer< QCPGraphDataContainer > data() const
Definition: qcustomplot.h:5190
void setCell(int keyIndex, int valueIndex, double z)
QCPRange mDataRange
Definition: qcustomplot.h:5794
LayerMode mMode
Definition: qcustomplot.h:681
virtual void getOptimizedLineData(QVector< QCPGraphData > *lineData, const QCPGraphDataContainer::const_iterator &begin, const QCPGraphDataContainer::const_iterator &end) const
PositionType mPositionTypeY
Definition: qcustomplot.h:3497
QCPLayerable * parentLayerable() const
Definition: qcustomplot.h:717
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
QPointer< QCPColorScaleAxisRectPrivate > mAxisRect
Definition: qcustomplot.h:5105
void setInsetRect(int index, const QRectF &rect)
QCPSelectionRect * mSelectionRect
Definition: qcustomplot.h:3785
line is drawn as steps where the step is in between two data points
Definition: qcustomplot.h:5181
Cosmetic pens are converted to pens with pixel width 1 when exporting.
Definition: qcustomplot.h:183
virtual bool take(QCPLayoutElement *element)=0
void setTextColor(const QColor &color)
int columnCount() const
Definition: qcustomplot.h:1362
void setData(QSharedPointer< QCPStatisticalBoxDataContainer > data)
virtual void keyPressEvent(QKeyEvent *event)
Days (%d in setTimeFormat)
Definition: qcustomplot.h:1656
void selectableChanged(bool selectable)
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:4675
When dragging the mouse, a selection rect becomes active. Upon releasing, the axes that are currently...
Definition: qcustomplot.h:276
void setBrush(const QBrush &brush)
void setSelectedFont(const QFont &font)
0x0100 Borders of fills (e.g. under or between graphs)
Definition: qcustomplot.h:230
0x004 The user can select multiple objects by holding the modifier set by QCustomPlot::setMultiSelect...
Definition: qcustomplot.h:261
void addTick(double position, QString label)
QCPAbstractPlottable * plottable()
QString label() const
QCPItemAnchor *const bottomRight
Definition: qcustomplot.h:6351
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE
void setBackgroundScaledMode(Qt::AspectRatioMode mode)
void setTicks(const QMap< double, QString > &ticks)
virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE
virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE
virtual ~QCPItemPixmap()
void setSpacing(double spacing)
QPointer< QCPAxis > mValueAxis
Definition: qcustomplot.h:3498
int begin() const
Definition: qcustomplot.h:908
Bar width is in key coordinates and thus scales with the key axis range.
Definition: qcustomplot.h:5500
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setRowSpacing(int pixels)
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const =0
The plottable is not selectable.
Definition: qcustomplot.h:299
QCPRange valueRange() const
Definition: qcustomplot.h:5700
QCPItemPosition * createPosition(const QString &name)
void setMinimumSize(const QSize &size)
QVector< double > mSubTickVector
Definition: qcustomplot.h:2176
void setWidthType(WidthType widthType)
QPen tickPen() const
Definition: qcustomplot.h:2037
QList< QCPBars * > mBars
Definition: qcustomplot.h:5430
int itemCount() const
virtual QPointF pixelPosition() const Q_DECL_OVERRIDE
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
QPen getSubTickPen() const
bool isEmpty() const
Definition: qcustomplot.h:5723
QCPDataRange dataRange(int index=0) const
QPen pen() const
Definition: qcustomplot.h:6316
LayerMode mode() const
Definition: qcustomplot.h:665
virtual ~QCPAbstractPaintBuffer()
QCPGraph * graph() const
QCPDataRange span() const
void setKeyAxis(QCPAxis *axis)
bool hasInvalidatedPaintBuffers()
QPointer< QCPLayerable > mMouseEventLayerable
Definition: qcustomplot.h:3792
virtual ~QCPItemCurve()
virtual ~QCPSelectionRect()
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
double width() const
Definition: qcustomplot.h:5897
int height() const
Definition: qcustomplot.h:4657
void fillAlpha(unsigned char alpha)
int dataRangeCount() const
Definition: qcustomplot.h:957
A layer that may contain objects, to control the rendering order.
Definition: qcustomplot.h:631
bool mTickLabels
Definition: qcustomplot.h:2151
Qt::Orientation orientation() const
Definition: qcustomplot.h:2108
Continuous lightness from black over firey colors to white (suited for non-biased data representation...
Definition: qcustomplot.h:4457
void setScaleStrategy(ScaleStrategy strategy)
void setEnd(int end)
Definition: qcustomplot.h:915
void setPen(const QPen &pen)
QCPItemPosition *const position
Definition: qcustomplot.h:6578
void rescaleDataRange(bool recalculateDataBounds=false)
virtual ~QCPItemRect()
virtual ~QCPErrorBars()
void setInverted(bool inverted)
LabelSide tickLabelSide() const
void setAutoMargins(QCP::MarginSides sides)
void getErrorBarLines(QCPErrorBarsDataContainer::const_iterator it, QVector< QLineF > &backbones, QVector< QLineF > &whiskers) const
virtual void axisRemoved(QCPAxis *axis)
0x00 Default mode for painting on screen devices
Definition: qcustomplot.h:472
Selection behaves like stMultipleDataRanges, but if there are any data points selected, the entire plottable is drawn as selected.
Definition: qcustomplot.h:300
Replots immediately and repaints the widget immediately by calling QWidget::repaint() after the replo...
Definition: qcustomplot.h:3621
QSize size() const
Definition: qcustomplot.h:530
LineStyle mLineStyle
Definition: qcustomplot.h:5351
QFont getLabelFont() const
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision)
SpacingType spacingType() const
Definition: qcustomplot.h:5407
The abstract base class for all objects that form the layout system.
Definition: qcustomplot.h:1187
virtual void drawScatterPlot(QCPPainter *painter, const QVector< QPointF > &points, const QCPScatterStyle &style) const
bool savePdf(const QString &fileName, int width=0, int height=0, QCP::ExportPen exportPen=QCP::epAllowCosmetic, const QString &pdfCreator=QString(), const QString &pdfTitle=QString())
QCPBarsGroup(QCustomPlot *parentPlot)
int getRegion(double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
PlottingHint
Definition: qcustomplot.h:243
QColor getLabelColor() const
QPen pen() const
Definition: qcustomplot.h:1112
void setGraph(QCPGraph *graph)
void mousePress(QMouseEvent *event)
QCPRange mValueRange
Definition: qcustomplot.h:5730
Continuous lightness from black to white (suited for non-biased data representation) ...
Definition: qcustomplot.h:4456
virtual int findBegin(double sortKey, bool expandedRange=true) const Q_DECL_OVERRIDE
void draw(QCPPainter *painter)
QCPTextElement(QCustomPlot *parentPlot)
void setFont(const QFont &font)
{ssPlus.png} a plus
Definition: qcustomplot.h:2329
static QDateTime keyToDateTime(double key)
int findIndexBelowX(const QVector< QPointF > *data, double x) const
void getDataSegments(QList< QCPDataRange > &selectedSegments, QList< QCPDataRange > &unselectedSegments) const
double pointDistance(const QPointF &pixelPoint, QCPCurveDataContainer::const_iterator &closestData) const
QCPAbstractPlottable(QCPAxis *keyAxis, QCPAxis *valueAxis)
QCP::AntialiasedElements mNotAADragBackup
Definition: qcustomplot.h:2181
QCPRange expanded(const QCPRange &otherRange) const
QCPPaintBufferPixmap(const QSize &size, double devicePixelRatio)
virtual QRect clipRect() const Q_DECL_OVERRIDE
Q_SLOT void setGradient(const QCPColorGradient &gradient)
void applyScattersAntialiasingHint(QCPPainter *painter) const
void getVisibleDataBounds(QCPStatisticalBoxDataContainer::const_iterator &begin, QCPStatisticalBoxDataContainer::const_iterator &end) const
QCPAxis * keyAxis() const
Definition: qcustomplot.h:3320
QBrush brush() const
Definition: qcustomplot.h:6318
void setOuterRect(const QRect &rect)
void setPen(const QPen &pen)
bool selected() const
Definition: qcustomplot.h:4961
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
bool mayTraverse(int prevRegion, int currentRegion) const
QCPLayer(QCustomPlot *parentPlot, const QString &layerName)
void setLineStyle(LineStyle ls)
int offset() const
QCPColorGradient gradient() const
Definition: qcustomplot.h:5766
int keySize() const
Definition: qcustomplot.h:5697
void setPen(const QPen &pen)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void updateLayerIndices() const
Blue over pink to white.
Definition: qcustomplot.h:4460
QCPLineEnding upperEnding
Definition: qcustomplot.h:2241
void selectableChanged(bool selectable)
QMap< double, QString > & ticks()
Definition: qcustomplot.h:1743
QString mLabel
Definition: qcustomplot.h:2146
void setTextColor(const QColor &color)
FillOrder mFillOrder
Definition: qcustomplot.h:1409
Q_SLOT void setSelectedParts(const QCPAxis::SelectableParts &selectedParts)
void getMaximumRowColSizes(QVector< int > *maxColWidths, QVector< int > *maxRowHeights) const
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
void insertColumn(int newIndex)
void setColumnSpacing(int pixels)
QRect getFinalRect(bool *flippedHorz=0, bool *flippedVert=0) const
virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE
Qt::AspectRatioMode mAspectRatioMode
Definition: qcustomplot.h:6499
QCPItemStraightLine(QCustomPlot *parentPlot)
Q_SLOT void rescaleAxes(bool onlyVisiblePlottables=false)
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
bool selected() const
Definition: qcustomplot.h:3323
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
QBrush mSelectedBrush
Definition: qcustomplot.h:6362
An integer power of the specified tick step is allowed.
Definition: qcustomplot.h:1707
QPen selectedIconBorderPen() const
Definition: qcustomplot.h:4851
virtual void draw(QCPPainter *painter)=0
Describes a data set by holding multiple QCPDataRange instances.
Definition: qcustomplot.h:935
QCPItemAnchor * parentAnchorX() const
Definition: qcustomplot.h:3472
double mRotation
Definition: qcustomplot.h:6367
int findIndexBelowY(const QVector< QPointF > *data, double y) const
QCPLayer * mCurrentLayer
Definition: qcustomplot.h:3781
virtual ~QCPAbstractItem()
QPainterPath mCustomPath
Definition: qcustomplot.h:2387
WidthType mWidthType
Definition: qcustomplot.h:5542
void setPen(const QPen &pen)
bool removeFromLegend() const
QCPBarsGroup * mBarsGroup
Definition: qcustomplot.h:5543
void dataScaleTypeChanged(QCPAxis::ScaleType scaleType)
QVector< QPointF > getOptimizedCornerPoints(int prevRegion, int currentRegion, double prevKey, double prevValue, double key, double value, double keyMin, double valueMax, double keyMax, double valueMin) const
virtual int dataCount() const =0
QCPItemAnchor *const left
Definition: qcustomplot.h:6354
virtual ~QCPItemAnchor()
double getStackedBaseValue(double key, bool positive) const
QFont mainFont() const
virtual void updateLayout()
virtual double dataSortKey(int index) const Q_DECL_OVERRIDE
void setTightBoundary(bool enabled)
bool mReplotting
Definition: qcustomplot.h:3796
bool operator==(const QCPColorGradient &other) const
QList< QCPAxis * > addAxes(QCPAxis::AxisTypes types)
QPen mSelectedTickPen
Definition: qcustomplot.h:2163
virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE
{ssCross.png} a cross
Definition: qcustomplot.h:2328
void setWidthType(WidthType widthType)
void setPeriodicity(int multiplesOfPi)
PainterModes mModes
Definition: qcustomplot.h:508
QPen mainPen() const
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
A brace with round edges.
Definition: qcustomplot.h:6622
A layout that places child elements aligned to the border or arbitrarily positioned.
Definition: qcustomplot.h:1421
void setData(QCPColorMapData *data, bool copy=false)
QPen getIconBorderPen() const
void setData(QSharedPointer< QCPGraphDataContainer > data)
void setViewport(const QRect &rect)
QCPAbstractPaintBuffer(const QSize &size, double devicePixelRatio)
0x020 Legends are selectable (or their child items, see QCPLegend::setSelectableParts) ...
Definition: qcustomplot.h:264
void setVisible(bool on)
void setStyle(TracerStyle style)
void setNoAntialiasingOnDrag(bool enabled)
Handles the different ending decorations for line-like items.
Definition: qcustomplot.h:1477
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE=0
A color scale for use with color coding data such as QCPColorMap.
Definition: qcustomplot.h:5046
double length() const
Definition: qcustomplot.h:409
virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE
virtual void update(UpdatePhase phase)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
Q_SLOT void setSelectable(bool selectable)
QPen pen() const
Definition: qcustomplot.h:6402
QPen mTickPen
Definition: qcustomplot.h:2163
QCP::PlottingHints mPlottingHints
Definition: qcustomplot.h:3782
virtual QCPPainter * startPainting() Q_DECL_OVERRIDE
BracketStyle mStyle
Definition: qcustomplot.h:6655
virtual QPointF dataPixelPosition(int index) const Q_DECL_OVERRIDE
QCPAxis * valueAxis() const
Definition: qcustomplot.h:3478
void setDateTimeSpec(Qt::TimeSpec spec)
QPointF getTextDrawPoint(const QPointF &pos, const QRectF &rect, Qt::Alignment positionAlignment) const
Rows are filled first, and a new element is wrapped to the next column if the row count would exceed ...
Definition: qcustomplot.h:1352
int mCachedMargin
Definition: qcustomplot.h:2178
bool selectable() const
Definition: qcustomplot.h:4960
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details) Q_DECL_OVERRIDE
virtual QCPItemPosition * toQCPItemPosition()
Definition: qcustomplot.h:3427
QPointer< QCPAxisRect > mClipAxisRect
Definition: qcustomplot.h:3555
QPointer< QCPLayerable > mMouseSignalLayerable
Definition: qcustomplot.h:3793
Q_SLOT void cancel()
QVector< QString > mTickVectorLabels
Definition: qcustomplot.h:2175
int getMarginValue(const QMargins &margins, QCP::MarginSide side)
Definition: qcustomplot.h:353
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const =0
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void axisClick(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
void draw(QCPPainter *painter, const QCPVector2D &pos, const QCPVector2D &dir) const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
Both sign domains, including zero, i.e. all numbers.
Definition: qcustomplot.h:195
virtual QList< QCPLayoutElement * > elements(bool recursive) const Q_DECL_OVERRIDE
bool setParentAnchorX(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
QList< QCPAbstractPlottable * > mPlottables
Definition: qcustomplot.h:3768
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:660
friend class QCPAxisRect
Definition: qcustomplot.h:3844
void applyTo(QCPPainter *painter, const QPen &defaultPen) const
QPen pen() const
Definition: qcustomplot.h:6632
QPen pen() const
Definition: qcustomplot.h:3318
QPainter subclass used internally.
Definition: qcustomplot.h:464
void append(QCPBars *bars)
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
Qt::TransformationMode transformationMode() const
Definition: qcustomplot.h:6469
void setTickLength(int inside, int outside=0)
int mScatterSkip
Definition: qcustomplot.h:5219
unsigned char * mAlpha
Definition: qcustomplot.h:5735
ScaleType mScaleType
Definition: qcustomplot.h:2168
bool selectable() const
Definition: qcustomplot.h:4737
Q_SLOT void setSelected(bool selected)
QPixmap mLegendIcon
Definition: qcustomplot.h:5804
QMap< double, QString > mTicks
Definition: qcustomplot.h:1759
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
QSet< QCPItemPosition * > mChildrenX
Definition: qcustomplot.h:3424
WidthType widthType() const
Definition: qcustomplot.h:5509
void canceled(const QRect &rect, QInputEvent *event)
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range) Q_DECL_OVERRIDE
bool operator==(const QCPDataSelection &other) const
QCPItemEllipse(QCustomPlot *parentPlot)
{ssCrossCircle.png} a circle with a cross inside
Definition: qcustomplot.h:2339
void setPlottingHints(const QCP::PlottingHints &hints)
void setMode(LayerMode mode)
void coordsToPixels(double key, double value, double &x, double &y) const
void addData(const QVector< double > &keys, const QVector< double > &values, bool alreadySorted=false)
void drawBackground(QCPPainter *painter)
double data(double key, double value)
Holds the data of one single data point (one bar) for QCPBars.
Definition: qcustomplot.h:5448
virtual QCPRange getKeyRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth) const Q_DECL_OVERRIDE
int left() const
Definition: qcustomplot.h:4652
bool mCachedMarginValid
Definition: qcustomplot.h:2177
double boundingDistance() const
QCPItemAnchor *const bottom
Definition: qcustomplot.h:6423
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
QPixmap mPixmap
Definition: qcustomplot.h:6495
virtual double getTickStep(const QCPRange &range)
QCPSelectionDecorator * mSelectionDecorator
Definition: qcustomplot.h:3372
QCPGrid(QCPAxis *parentAxis)
QCPScatterStyle::ScatterProperties usedScatterProperties() const
Definition: qcustomplot.h:3259
void setColorInterpolation(ColorInterpolation interpolation)
QColor mSelectedColor
Definition: qcustomplot.h:6360
QPen mSelectedBasePen
Definition: qcustomplot.h:2142
QCPLineEnding head() const
Definition: qcustomplot.h:6147
{ssDot.png} a single pixel (use ssDisc or ssCircle if you want a round shape with a certain radius) ...
Definition: qcustomplot.h:2327
void normalize()
Definition: qcustomplot.h:804
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection)
virtual QCPLayoutElement * elementAt(int index) const Q_DECL_OVERRIDE
Layer has its own paint buffer and may be replotted individually (see replot).
Definition: qcustomplot.h:652
QPen mainPen() const
Minutes (%m in setTimeFormat)
Definition: qcustomplot.h:1654
int mPadding
Definition: qcustomplot.h:2139
void setChartStyle(ChartStyle style)
void setBarsGroup(QCPBarsGroup *barsGroup)
QVector< QPair< QCPDataRange, QCPDataRange > > getOverlappingSegments(QVector< QCPDataRange > thisSegments, const QVector< QPointF > *thisData, QVector< QCPDataRange > otherSegments, const QVector< QPointF > *otherData) const
void setPiSymbol(QString symbol)
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
static QSize getFinalMaximumOuterSize(const QCPLayoutElement *el)
virtual QVector< double > createTickVector(double tickStep, const QCPRange &range)
QCPAxis * yAxis2
Definition: qcustomplot.h:3739
SizeConstraintRect mSizeConstraintRect
Definition: qcustomplot.h:1265
QColor mainTextColor() const
QVector< QRgb > mColorBuffer
Definition: qcustomplot.h:4505
void registerBars(QCPBars *bars)
QCPAxisRect * axisRect() const
Definition: qcustomplot.h:2015
QCPAbstractItem * mParentItem
Definition: qcustomplot.h:3422
virtual bool sortKeyIsMainKey() const Q_DECL_OVERRIDE
{ssPlusSquare.png} a square with a plus inside
Definition: qcustomplot.h:2338
A filled diamond (45 degrees rotated square)
Definition: qcustomplot.h:1498
QSharedPointer< QCPErrorBarsDataContainer > data() const
Definition: qcustomplot.h:6018
void setIconBorderPen(const QPen &pen)
double lengthSquared() const
Definition: qcustomplot.h:410
QCP::SelectionRectMode mSelectionRectMode
Definition: qcustomplot.h:3784
QPointF getPixelCoordinates(const QCPPlottableInterface1D *interface1d, int dataIndex) const
void removeChildY(QCPItemPosition *pos)
Colors suitable to represent different elevations on geographical maps.
Definition: qcustomplot.h:4461
int selectionTolerance() const
Definition: qcustomplot.h:3642
void setSelectedFont(const QFont &font)
Qt::TimeSpec mDateTimeSpec
Definition: qcustomplot.h:1625
QCPItemAnchor(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name, int anchorId=-1)
QCPAxis * xAxis2
Definition: qcustomplot.h:3739
QPen mSelectedSubTickPen
Definition: qcustomplot.h:2164
QCPVector2D & operator/=(double divisor)
QBrush mSelectedBrush
Definition: qcustomplot.h:6433
0x01 The pen property, see setPen
Definition: qcustomplot.h:2309
void setNumberPrecision(int precision)
QVector< QLineF > getWhiskerBarLines(QCPStatisticalBoxDataContainer::const_iterator it) const
bool errorBarVisible(int index) const
QRect labelSelectionBox() const
Definition: qcustomplot.h:2236
static const double minRange
Definition: qcustomplot.h:816
QList< QCPAbstractItem * > items() const
void setSelectedPen(const QPen &pen)
QCPLayer * currentLayer() const
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
void setOpenGl(bool enabled, int multisampling=16)
virtual void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE
void setKeyRange(const QCPRange &keyRange)
virtual QSize maximumOuterSizeHint() const
int layerCount() const
{ssTriangle.png} an equilateral triangle, standing on baseline
Definition: qcustomplot.h:2335
{ssDisc.png} a circle which is filled with the pen&#39;s color (not the brush as with ssCircle) ...
Definition: qcustomplot.h:2331
QCPGraph * graph() const
Definition: qcustomplot.h:6557
QCPAxis::AxisType type
Definition: qcustomplot.h:2239
Q_SLOT void setSelected(bool selected)
bool isNone() const
Definition: qcustomplot.h:2373
void legendClick(QCPLegend *legend, QCPAbstractLegendItem *item, QMouseEvent *event)
void rescaleAxes(bool onlyEnlarge=false) const
0x0020 Main lines of plottables
Definition: qcustomplot.h:227
friend class QCPLayer
Definition: qcustomplot.h:3843
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const =0
QCPRange mKeyRange
Definition: qcustomplot.h:5730
virtual void update(UpdatePhase phase) Q_DECL_OVERRIDE
Qt::Orientations rangeZoom() const
Definition: qcustomplot.h:4610
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
Interaction
Definition: qcustomplot.h:259
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
static void connectBars(QCPBars *lower, QCPBars *upper)
virtual void drawLinePlot(QCPPainter *painter, const QVector< QPointF > &lines) const
QVector< DataType >::const_iterator const_iterator
Definition: qcustomplot.h:2415
bool createAlpha(bool initializeOpaque=true)
QCPBars * barBelow() const
Definition: qcustomplot.h:5513
bool rangeReversed() const
Definition: qcustomplot.h:2018
void setBrush(const QBrush &brush)
int top() const
Definition: qcustomplot.h:4654
QPen mSelectedPen
Definition: qcustomplot.h:6164
A bar perpendicular to the line.
Definition: qcustomplot.h:1499
Q_SLOT void setSelectedParts(const SelectableParts &selectedParts)
void setRangeDrag(bool enabled)
QBrush mBrushPositive
Definition: qcustomplot.h:5936
QPen pen() const
Definition: qcustomplot.h:6551
virtual ~QCPSelectionDecorator()
data points are connected by a straight line
Definition: qcustomplot.h:5178
Final phase in which the layout system places the rects of the elements.
Definition: qcustomplot.h:1207
0x01 Axis is vertical and on the left side of the axis rect
Definition: qcustomplot.h:1972
void setLabelColor(const QColor &color)
void setAntialiasedFill(bool enabled)
QCP::AntialiasedElements mAntialiasedElements
Definition: qcustomplot.h:3772
bool inverted() const
Definition: qcustomplot.h:1512
QCPItemPosition *const topLeft
Definition: qcustomplot.h:6482
void setHead(const QCPLineEnding &head)
void setNotAntialiasedElements(const QCP::AntialiasedElements &notAntialiasedElements)
QCPItemAnchor *const bottom
Definition: qcustomplot.h:6265
bool mAutoAddPlottableToLegend
Definition: qcustomplot.h:3767
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void setAntialiased(bool enabled)
void setSelectedBrush(const QBrush &brush)
QSharedPointer< QCPBarsDataContainer > data() const
Definition: qcustomplot.h:5515
QCustomPlot * parentPlot() const
Definition: qcustomplot.h:716
static QCPFinancialDataContainer timeSeriesToOhlc(const QVector< double > &time, const QVector< double > &value, double timeBinSize, double timeBinOffset=0)
QPen pen() const
Definition: qcustomplot.h:1886
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QCPItemPixmap(QCustomPlot *parentPlot)
QCPLayout * layout() const
Definition: qcustomplot.h:1228
QCPAxis * rangeDragAxis(Qt::Orientation orientation)
QCPAxisRect * axisRect(int index=0) const
void setFractionStyle(FractionStyle style)
0x0000 No elements
Definition: qcustomplot.h:234
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged) Q_DECL_OVERRIDE
bool contains(const QCPDataRange &other) const
void setFont(const QFont &font)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setRowStretchFactor(int row, double factor)
QCPAbstractPlottable * plottableAt(const QPointF &pos, bool onlySelectable=false) const
void setBarWidth(int width)
QList< double > mColumnStretchFactors
Definition: qcustomplot.h:1405
bool mMapImageInvalidated
Definition: qcustomplot.h:5805
BracketStyle style() const
Definition: qcustomplot.h:6635
QList< QCPGraph * > selectedGraphs() const
QCPItemAnchor *const top
Definition: qcustomplot.h:6348
bool removeItem(QCPAbstractItem *item)
void drawBackground(QCPPainter *painter)
Bar width is given by a fraction of the axis rect size.
Definition: qcustomplot.h:5499
0x02 right margin
Definition: qcustomplot.h:205
void setInterpolate(bool enabled)
SelectableParts selectedParts() const
Definition: qcustomplot.h:2045
void setPen(const QPen &pen)
Q_SLOT void deselectAll()
QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis)
QRectF getQuartileBox(QCPStatisticalBoxDataContainer::const_iterator it) const
QPointer< QCPColorScale > mColorScale
Definition: qcustomplot.h:5800
void setBufferDevicePixelRatio(double ratio)
QCPScatterStyle scatterStyle() const
Definition: qcustomplot.h:3258
bool selected() const
Definition: qcustomplot.h:3530
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos)
QCPLineEnding mTail
Definition: qcustomplot.h:6165
void setTickLabelSide(LabelSide side)
QCPLayoutInset * insetLayout() const
Definition: qcustomplot.h:4642
QCPRange mDataRange
Definition: qcustomplot.h:5099
virtual void deselectEvent(bool *selectionStateChanged) Q_DECL_OVERRIDE
virtual ~QCPItemBracket()
void removeChild(QCP::MarginSide side, QCPLayoutElement *element)
void setupPaintBuffers()
void setBrush(const QBrush &brush)
void setPixelPosition(const QPointF &pixelPosition)
void setSelectionTolerance(int pixels)
void setSelectedPen(const QPen &pen)
void clear(const QColor &color) Q_DECL_OVERRIDE
void setPen(const QPen &pen)
static AxisType opposite(AxisType type)
QString name() const
Definition: qcustomplot.h:3315
void setSymbolGap(double pixels)
Less readable tick steps are allowed which in turn facilitates getting closer to the requested tick c...
Definition: qcustomplot.h:1553
void gradientChanged(const QCPColorGradient &newGradient)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
int mNumberPrecision
Definition: qcustomplot.h:2155
virtual void draw(QCPPainter *painter)
double pixelToCoord(double value) const
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
enum QCPAxisTickerDateTime::DateStrategy mDateStrategy
void setWhiskerAntialiased(bool enabled)
QMap< double, QColor > mColorStops
Definition: qcustomplot.h:4500
{ssStar.png} a star with eight arms, i.e. a combination of cross and plus
Definition: qcustomplot.h:2334
void addTicks(const QMap< double, QString > &ticks)
QStack< bool > mAntialiasingStack
Definition: qcustomplot.h:512
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
void cellToCoord(int keyIndex, int valueIndex, double *key, double *value) const
QPen mainPen() const
QSize iconSize() const
Definition: qcustomplot.h:4845
void clearItems()
Phase used for any type of preparation that needs to be done before margin calculation and layout...
Definition: qcustomplot.h:1205
QList< QPointer< QCPAxis > > mRangeZoomHorzAxis
Definition: qcustomplot.h:4679
virtual void layoutChanged()
bool moveLayer(QCPLayer *layer, QCPLayer *otherLayer, LayerInsertMode insertMode=limAbove)
void selectableChanged(bool selectable)
void drawCandlestickPlot(QCPPainter *painter, const QCPFinancialDataContainer::const_iterator &begin, const QCPFinancialDataContainer::const_iterator &end, bool isSelected)
double mRangeZoomFactorHorz
Definition: qcustomplot.h:4680
Q_SLOT void setDataRange(const QCPRange &dataRange)
virtual int calculateAutoMargin(QCP::MarginSide side)
void setSelectedTextColor(const QColor &color)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void setTickLengthOut(int outside)
double rangeZoomFactor(Qt::Orientation orientation)
QCPItemPosition *const point1
Definition: qcustomplot.h:6110
void setMultiSelectModifier(Qt::KeyboardModifier modifier)
virtual QVector< QString > createLabelVector(const QVector< double > &ticks, const QLocale &locale, QChar formatChar, int precision)
QPen getBorderPen() const
QList< QCPColorMap * > colorMaps() const
bool hasItem(QCPAbstractLegendItem *item) const
virtual void legendRemoved(QCPLegend *legend)
virtual double dataMainKey(int index) const Q_DECL_OVERRIDE
bool mSubGridVisible
Definition: qcustomplot.h:1900
void unregisterBars(QCPBars *bars)
void setSelectedTickPen(const QPen &pen)
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:5795
void setSelectedFont(const QFont &font)
Describes a data range given by begin and end index.
Definition: qcustomplot.h:898
virtual void simplify()
void moveRange(double diff)
void setLabelFont(const QFont &font)
QCP::SelectionType mSelectable
Definition: qcustomplot.h:3370
A bar perpendicular to the line, pointing out to only one side (to which side can be changed with set...
Definition: qcustomplot.h:1500
0x002 Legend items individually (see selectedItems)
Definition: qcustomplot.h:4831
virtual void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE
QPixmap pixmap() const
Definition: qcustomplot.h:2360
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
QCPAxis::ScaleType mDataScaleType
Definition: qcustomplot.h:5100
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
QList< QCPLegend * > selectedLegends() const
void restore()
QCPAbstractItem * item() const
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
void addChildY(QCPItemPosition *pos)
QWeakPointer< QCPAbstractPaintBuffer > mPaintBuffer
Definition: qcustomplot.h:684
{ssPlusCircle.png} a circle with a plus inside
Definition: qcustomplot.h:2340
QSharedPointer< QCPErrorBarsDataContainer > mDataContainer
Definition: qcustomplot.h:6057
QPen pen() const
Definition: qcustomplot.h:6470
bool mBackgroundScaled
Definition: qcustomplot.h:3779
QRect rect() const
Definition: qcustomplot.h:1229
void setRotation(double degrees)
QRectF getBarRect(double key, double value) const
void setSubTickPen(const QPen &pen)
{ssCrossSquare.png} a square with a cross inside
Definition: qcustomplot.h:2337
QHash< QCPAxis::AxisType, QList< QCPAxis * > > mAxes
Definition: qcustomplot.h:4686
QString name() const
Definition: qcustomplot.h:3413
QCPItemAnchor *const right
Definition: qcustomplot.h:6350
void setLength(double length)
void setBracketHeight(int height)
virtual QSize maximumOuterSizeHint() const Q_DECL_OVERRIDE
virtual ~QCPAbstractPlottable()
virtual void getOptimizedScatterData(QVector< QCPGraphData > *scatterData, QCPGraphDataContainer::const_iterator begin, QCPGraphDataContainer::const_iterator end) const
Data points are connected with a straight line.
Definition: qcustomplot.h:5315
QBrush mSelectedBrush
Definition: qcustomplot.h:6274
Dynamic positioning at a plot coordinate defined by two axes (see setAxes).
Definition: qcustomplot.h:3460
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
virtual int getSubTickCount(double tickStep)
QCPDataSelection mSelection
Definition: qcustomplot.h:3371
void setNumberFormat(const QString &formatCode)
virtual QSize minimumOuterSizeHint() const
SelectableParts mSelectableParts
Definition: qcustomplot.h:2141
bool visible() const
Definition: qcustomplot.h:664
QCPItemPosition *const startDir
Definition: qcustomplot.h:6210
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QCPItemPosition(QCustomPlot *parentPlot, QCPAbstractItem *parentItem, const QString &name)
virtual QRect clipRect() const Q_DECL_OVERRIDE
The axis label.
Definition: qcustomplot.h:2004
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
QCPRange sanitizedForLinScale() const
void setLineStyle(LineStyle style)
QCPSelectionRect * selectionRect() const
Definition: qcustomplot.h:3647
virtual void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE
bool isValid() const
Definition: qcustomplot.h:918
bool mAntialiased
Definition: qcustomplot.h:742
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details)
Colors suitable to emphasize polarity around the center, with blue for negative, black in the middle ...
Definition: qcustomplot.h:4464
void scaleTypeChanged(QCPAxis::ScaleType scaleType)
void replaceUnit(QString &text, TimeUnit unit, int value) const
QCPLayoutElement * element(int row, int column) const
QPen pen() const
Definition: qcustomplot.h:2358
The generic data container for one-dimensional plottables.
Definition: qcustomplot.h:2412
void setLogBase(double base)
QCPAxisPainterPrivate(QCustomPlot *parentPlot)
Qt::Alignment insetAlignment(int index) const
void add(const QCPDataContainer< DataType > &data)
Definition: qcustomplot.h:2672
double pickClosest(double target, const QVector< double > &candidates) const
SelectablePart getPartAt(const QPointF &pos) const
QPen mainPen() const
bool intersects(const QCPDataRange &other) const
void accepted(const QRect &rect, QMouseEvent *event)
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos)
void setTail(const QCPLineEnding &tail)
Holds the two-dimensional data of a QCPColorMap plottable.
Definition: qcustomplot.h:5688
bool setParentAnchor(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
QBrush mainBrush() const
void setSelectedFont(const QFont &font)
QList< QCPAbstractItem * > mItems
Definition: qcustomplot.h:3770
virtual ~QCPLayoutGrid()
bool rectIntersectsLine(const QRectF &pixelRect, const QLineF &line) const
0x002 Axis ranges are zoomable with the mouse wheel (see QCPAxisRect::setRangeZoom, QCPAxisRect::setRangeZoomAxes)
Definition: qcustomplot.h:260
QCPAbstractLegendItem * item(int index) const
virtual void startSelection(QMouseEvent *event)
void getVisibleDataBounds(QCPFinancialDataContainer::const_iterator &begin, QCPFinancialDataContainer::const_iterator &end) const
QCPItemAnchor *const top
Definition: qcustomplot.h:6419
virtual QVector< double > createSubTickVector(int subTickCount, const QVector< double > &ticks)
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
void addDataRange(const QCPDataRange &dataRange, bool simplify=true)
QString numberFormat() const
width is given by a fraction of the axis rect size
Definition: qcustomplot.h:5876
void setTickLengthIn(int inside)
virtual void endSelection(QMouseEvent *event)
double mRangeZoomFactorVert
Definition: qcustomplot.h:4680
void setSelectedColor(const QColor &color)
double size() const
Definition: qcustomplot.h:6555
void setSelectedPen(const QPen &pen)
void setRangeZoomFactor(double horizontalFactor, double verticalFactor)
void addData(const QVector< double > &keys, const QVector< double > &open, const QVector< double > &high, const QVector< double > &low, const QVector< double > &close, bool alreadySorted=false)
void setSelectedBrush(const QBrush &brush)
virtual void copyFrom(const QCPSelectionDecorator *other)
Q_SLOT void setScaleType(QCPAxis::ScaleType type)
void setClipToAxisRect(bool clip)
TracerStyle style() const
Definition: qcustomplot.h:6556
a custom pixmap specified by setPixmap, centered on the data point coordinates
Definition: qcustomplot.h:2342
void setColumnStretchFactors(const QList< double > &factors)
void mouseWheel(QWheelEvent *event)
bool addItem(QCPAbstractLegendItem *item)
0x0010 Legend items
Definition: qcustomplot.h:226
Continuous lightness from black over weak blueish colors to white (suited for non-biased data represe...
Definition: qcustomplot.h:4459
QBrush brush() const
Definition: qcustomplot.h:3257
void setMarginValue(QMargins &margins, QCP::MarginSide side, int value)
Definition: qcustomplot.h:333
void beforeReplot()
void setPen(const QPen &pen)
void drawPolyline(QCPPainter *painter, const QVector< QPointF > &lineData) const
QFont getTickLabelFont() const
QCPAxisPainterPrivate * mAxisPainter
Definition: qcustomplot.h:2172
void setWhiskerWidth(double pixels)
virtual QPointF anchorPixelPosition(int anchorId) const
void setMode(PainterMode mode, bool enabled=true)
The abstract base class for layouts.
Definition: qcustomplot.h:1290
void setSubGridVisible(bool visible)
int rowCount() const
Definition: qcustomplot.h:1361
QString unicodeSubscript(int number) const
Holds the data of one single data point for QCPFinancial.
Definition: qcustomplot.h:5824
void setTickLabelPadding(int padding)
QBrush mBackgroundBrush
Definition: qcustomplot.h:3776
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
QLineF getRectClippedStraightLine(const QCPVector2D &point1, const QCPVector2D &vec, const QRect &rect) const
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void setSelectedLabelColor(const QColor &color)
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:6483
None of the selectable parts.
Definition: qcustomplot.h:2001
void setMarginGroup(QCP::MarginSides sides, QCPMarginGroup *group)
QCPLayoutElement * layoutElementAt(const QPointF &pos) const
void setInteractions(const QCP::Interactions &interactions)
Any combination of data points/ranges can be selected.
Definition: qcustomplot.h:303
void setBrush(const QBrush &brush)
0x04 Axis is horizontal and on the top side of the axis rect
Definition: qcustomplot.h:1974
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
virtual void selectEvent(QMouseEvent *event, bool additive, const QVariant &details, bool *selectionStateChanged)
Resolution is given in dots per centimeter (dpcm)
Definition: qcustomplot.h:174
bool mOpenGlCacheLabelsBackup
Definition: qcustomplot.h:3800
virtual int elementCount() const Q_DECL_OVERRIDE
Definition: qcustomplot.h:1382
bool realVisibility() const
QCPItemAnchor *const left
Definition: qcustomplot.h:6267
QCPAbstractPlottable * mPlottable
Definition: qcustomplot.h:4791
QCPAbstractPlottable * plottable(int index)
virtual void mouseReleaseEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
ColorInterpolation mColorInterpolation
Definition: qcustomplot.h:4501
QCPPlottableLegendItem * itemWithPlottable(const QCPAbstractPlottable *plottable) const
QString unicodeSuperscript(int number) const
QSize size() const
Definition: qcustomplot.h:4658
QCPItemAnchor *const bottomLeft
Definition: qcustomplot.h:6266
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
virtual double dataMainValue(int index) const Q_DECL_OVERRIDE
QCPAxis * axis(QCPAxis::AxisType type, int index=0) const
{ssTriangleInverted.png} an equilateral triangle, standing on corner
Definition: qcustomplot.h:2336
void setLength(double length)
void setAlpha(int keyIndex, int valueIndex, unsigned char alpha)
QVariant mMouseSignalLayerableDetails
Definition: qcustomplot.h:3795
double x() const
Definition: qcustomplot.h:399
A plottable representing a two-dimensional color map in a plot.
Definition: qcustomplot.h:5745
Layer is inserted above other layer.
Definition: qcustomplot.h:3612
void rescale(bool onlyVisiblePlottables=false)
bool mAdaptiveSampling
Definition: qcustomplot.h:5221
QHash< QCP::MarginSide, QList< QCPLayoutElement * > > mChildren
Definition: qcustomplot.h:1171
void setPlottingHint(QCP::PlottingHint hint, bool enabled=true)
void setTextFlags(int flags)
QCPAxis * valueAxis() const
Definition: qcustomplot.h:3321
QColor mTickLabelColor
Definition: qcustomplot.h:2154
virtual void drawLegendIcon(QCPPainter *painter, const QRectF &rect) const Q_DECL_OVERRIDE
QPointer< QCPAxis > mKeyAxis
Definition: qcustomplot.h:3498
InsetPlacement insetPlacement(int index) const
QRect viewport() const
Definition: qcustomplot.h:3632
void setScaled(bool scaled, Qt::AspectRatioMode aspectRatioMode=Qt::KeepAspectRatio, Qt::TransformationMode transformationMode=Qt::SmoothTransformation)
void setColumnStretchFactor(int column, double factor)
void setSelectedPen(const QPen &pen)
void setBracketStyle(BracketStyle style)
QCPDataSelection & operator+=(const QCPDataSelection &other)
bool mVisible
Definition: qcustomplot.h:680
double distanceToStraightLine(const QCPVector2D &base, const QCPVector2D &direction) const
bool setCurrentLayer(const QString &name)
QCPItemPosition *const position
Definition: qcustomplot.h:6346
void setAxisRect(QCPAxisRect *axisRect)
virtual void mousePressEvent(QMouseEvent *event, const QVariant &details)
int tickLabelPadding() const
void addChildX(QCPItemPosition *pos)
The positive sign domain, i.e. numbers greater than zero.
Definition: qcustomplot.h:196
QCPAxis::LabelSide tickLabelSide
Definition: qcustomplot.h:2248
QPointer< QCPLayerable > mParentLayerable
Definition: qcustomplot.h:740
QCPCurve(QCPAxis *keyAxis, QCPAxis *valueAxis)
int subTickLengthOut() const
QList< QCPAbstractPlottable * > plottables() const
QCPItemAnchor *const topLeft
Definition: qcustomplot.h:6347
QPen mZeroLinePen
Definition: qcustomplot.h:1902
QPen mainPen() const
QPoint mMousePressPos
Definition: qcustomplot.h:3790
bool isNull() const
Definition: qcustomplot.h:414
Color channels red, green and blue are linearly interpolated.
Definition: qcustomplot.h:4447
bool ticks() const
Definition: qcustomplot.h:2020
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
QCPItemAnchor *const right
Definition: qcustomplot.h:6421
WidthType widthType() const
Definition: qcustomplot.h:5898
Tick labels will be displayed inside the axis rect and clipped to the inner axis rect.
Definition: qcustomplot.h:1985
ChartStyle mChartStyle
Definition: qcustomplot.h:5932
void doubleClicked(QMouseEvent *event)
QList< double > mRowStretchFactors
Definition: qcustomplot.h:1406
void setInsetAlignment(int index, Qt::Alignment alignment)
void setData(QSharedPointer< QCPFinancialDataContainer > data)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
QRect mViewport
Definition: qcustomplot.h:3764
void setPen(const QPen &pen)
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:4673
When dragging the mouse, a selection rect becomes active. Upon releasing, plottable data points that ...
Definition: qcustomplot.h:277
friend class QCPLegend
Definition: qcustomplot.h:3841
QCPColorMap(QCPAxis *keyAxis, QCPAxis *valueAxis)
bool removeAxis(QCPAxis *axis)
double key() const
Definition: qcustomplot.h:3474
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
The negative sign domain, i.e. numbers smaller than zero.
Definition: qcustomplot.h:194
void setRangeZoom(bool enabled)
virtual void mouseDoubleClickEvent(QMouseEvent *event, const QVariant &details)
virtual QString getTickLabel(double tick, const QLocale &locale, QChar formatChar, int precision) Q_DECL_OVERRIDE
double value
Definition: qcustomplot.h:5463
void setAutoAddPlottableToLegend(bool on)
QPointer< QCPAxisRect > mAxisRect
Definition: qcustomplot.h:3499
void setClipAxisRect(QCPAxisRect *rect)
Qt::TransformationMode mTransformationMode
Definition: qcustomplot.h:6500
virtual Q_SLOT void processPointSelection(QMouseEvent *event)
double baseValue() const
Definition: qcustomplot.h:5511
virtual int calculateMargin()
void setText(const QString &text)
QPen pen() const
Definition: qcustomplot.h:6100
QFont mTickLabelFont
Definition: qcustomplot.h:2153
void setPen(const QPen &pen)
QVector< double > tickPositions
Definition: qcustomplot.h:2261
QCPDataSelection intersection(const QCPDataRange &other) const
QCPAxisRect(QCustomPlot *parentPlot, bool setupDefaultAxes=true)
width is in absolute pixels
Definition: qcustomplot.h:5875
void selectionChanged(bool selected)
QHash< TimeUnit, int > mFieldWidth
Definition: qcustomplot.h:1673
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
QPen pen() const
Definition: qcustomplot.h:6246
void setSelectedTextColor(const QColor &color)
virtual void getMaxTickLabelSize(const QFont &font, const QString &text, QSize *tickLabelsSize) const
QCPDataSelection inverse(const QCPDataRange &outerRange) const
void expand(const QCPRange &otherRange)
QCPAbstractItem * itemAt(const QPointF &pos, bool onlySelectable=false) const
QCPRange dataBounds() const
Definition: qcustomplot.h:5701
QCPLayerable * layerableAt(const QPointF &pos, bool onlySelectable, QVariant *selectionDetails=0) const
int size() const
Definition: qcustomplot.h:910
Holds the data of one single data point for QCPStatisticalBox.
Definition: qcustomplot.h:5571
bool mReplotQueued
Definition: qcustomplot.h:3797
A non-filled arrow head with open back.
Definition: qcustomplot.h:1495
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
0x04 The size property, see setSize
Definition: qcustomplot.h:2311
virtual void updateLayout() Q_DECL_OVERRIDE
A paint buffer based on QPixmap, using software raster rendering.
Definition: qcustomplot.h:558
void setSelectedPen(const QPen &pen)
void setBorderPen(const QPen &pen)
A nicely readable tick step is prioritized over matching the requested number of ticks (see setTickCo...
Definition: qcustomplot.h:1552
SelectableParts selectableParts() const
Definition: qcustomplot.h:2046
void setZeroLinePen(const QPen &pen)
QCPItemPosition *const point2
Definition: qcustomplot.h:6111
bool hasAnchor(const QString &name) const
virtual QCPDataSelection selectTestRect(const QRectF &rect, bool onlySelectable) const Q_DECL_OVERRIDE
virtual QCPLayoutElement * takeAt(int index) Q_DECL_OVERRIDE
QVariant mMouseEventLayerableDetails
Definition: qcustomplot.h:3794
Bar spacing is in absolute pixels.
Definition: qcustomplot.h:5397
QMargins mPadding
Definition: qcustomplot.h:6368
QCPRange mDataBounds
Definition: qcustomplot.h:5736
void setFillOrder(FillOrder order, bool rearrange=true)
double length() const
Definition: qcustomplot.h:1511
void selectableChanged(const QCPAxis::SelectableParts &parts)
void setIconSize(const QSize &size)
void setBracketPen(const QPen &pen)
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE
void setBaseValue(double baseValue)
QCPItemAnchor * mParentAnchorY
Definition: qcustomplot.h:3501
0x0200 Zero-lines, see QCPGrid::setZeroLinePen
Definition: qcustomplot.h:231
int mOpenGlMultisamples
Definition: qcustomplot.h:3798
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
bool addLayer(const QString &name, QCPLayer *otherLayer=0, LayerInsertMode insertMode=limAbove)
QString mText
Definition: qcustomplot.h:6364
void setSelectedPen(const QPen &pen)
double coordToPixel(double value) const
virtual void draw(QCPPainter *painter) const Q_DECL_OVERRIDE
void setScatterStyle(const QCPScatterStyle &style)
QList< QCPAxis * > axes(QCPAxis::AxisTypes types) const
void getLines(QVector< QPointF > *lines, const QCPDataRange &dataRange) const
QBrush getBrush() const
virtual ~QCPItemPosition()
Manages the position of an item.
Definition: qcustomplot.h:3443
unsigned char alpha(int keyIndex, int valueIndex)
void adoptElement(QCPLayoutElement *el)
void setValueRange(const QCPRange &valueRange)
QList< QCPRange > mDragStartVertRange
Definition: qcustomplot.h:4683
virtual void drawDecoration(QCPPainter *painter, QCPDataSelection selection) Q_DECL_OVERRIDE
bool hasItem(QCPAbstractItem *item) const
void setDataPlottable(QCPAbstractPlottable *plottable)
ResolutionUnit
Definition: qcustomplot.h:173
virtual void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE
void drawToPaintBuffer()
A half ellipse is drawn. The size of the ellipse is given by the bracket width/height properties...
Definition: qcustomplot.h:4533
void setSubTickCount(int subTicks)
QPen iconBorderPen() const
Definition: qcustomplot.h:4847
QCPItemPosition *const end
Definition: qcustomplot.h:6160
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
A curly brace with varying stroke width giving a calligraphic impression.
Definition: qcustomplot.h:6624
QList< QCPAbstractItem * > items() const
void setSize(double size)
0x02 The brush property, see setBrush
Definition: qcustomplot.h:2310
void getDataSegments(QList< QCPDataRange > &selectedSegments, QList< QCPDataRange > &unselectedSegments) const
QCPDataContainer< QCPFinancialData > QCPFinancialDataContainer
Definition: qcustomplot.h:5853
QPixmap mBackgroundPixmap
Definition: qcustomplot.h:4672
void setTicks(bool show)
QFont mSelectedFont
Definition: qcustomplot.h:6363
void setGraphKey(double key)
Defines a color gradient for use with e.g. QCPColorMap.
Definition: qcustomplot.h:4438
virtual int dataCount() const Q_DECL_OVERRIDE
QCPMarginGroup(QCustomPlot *parentPlot)
QCPErrorBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
void setTypeY(PositionType type)
QRectF selectionHitBox(QCPFinancialDataContainer::const_iterator it) const
QCPGrid * grid() const
Definition: qcustomplot.h:2056
double upper
Definition: qcustomplot.h:783
virtual void mouseDoubleClickEvent(QMouseEvent *event) Q_DECL_OVERRIDE
int tickLengthIn() const
Resolution is given in dots per inch (DPI/PPI)
Definition: qcustomplot.h:175
QCPMarginGroup * marginGroup(QCP::MarginSide side) const
Definition: qcustomplot.h:1237
QLineF getRectClippedLine(const QCPVector2D &start, const QCPVector2D &end, const QRect &rect) const
Q_SLOT void setDataScaleType(QCPAxis::ScaleType scaleType)
QPixmap mScaledBackgroundPixmap
Definition: qcustomplot.h:3778
virtual Q_SLOT void processRectZoom(QRect rect, QMouseEvent *event)
QCPAbstractItem(QCustomPlot *parentPlot)
void setSubTickLength(int inside, int outside=0)
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
void setBrushPositive(const QBrush &brush)
QMap< double, QColor > colorStops() const
Definition: qcustomplot.h:4478
QMargins mMinimumMargins
Definition: qcustomplot.h:1267
virtual void drawStatisticalBox(QCPPainter *painter, QCPStatisticalBoxDataContainer::const_iterator it, const QCPScatterStyle &outlierStyle) const
A bar that is skewed (skew controllable via setLength)
Definition: qcustomplot.h:1501
virtual void parentPlotInitialized(QCustomPlot *parentPlot)
Represents the range an axis is encompassing.
Definition: qcustomplot.h:780
QCPRange mDragStartRange
Definition: qcustomplot.h:2180
QColor mLabelColor
Definition: qcustomplot.h:2148
virtual int getSubTickCount(double tickStep) Q_DECL_OVERRIDE
0x8000 Other elements that don&#39;t fit into any of the existing categories
Definition: qcustomplot.h:232
virtual QCP::Interaction selectionCategory() const Q_DECL_OVERRIDE
void addData(const QVector< double > &error)
QCPRange dataRange() const
Definition: qcustomplot.h:5762
bool tickLabels() const
Definition: qcustomplot.h:2021
QCPLayoutGrid * mPlotLayout
Definition: qcustomplot.h:3766
virtual ~QCPBarsGroup()
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void setAntialiasedZeroLine(bool enabled)
QCP::AntialiasedElements mAADragBackup
Definition: qcustomplot.h:2181
QVector< QLineF > getWhiskerBackboneLines(QCPStatisticalBoxDataContainer::const_iterator it) const
Definicja QCustomPlot.
void setPositionAlignment(Qt::Alignment alignment)
QList< QCPItemAnchor * > mAnchors
Definition: qcustomplot.h:3557
QCPScatterStyle mScatterStyle
Definition: qcustomplot.h:5218
QList< QCPAxis * > rangeZoomAxes(Qt::Orientation orientation)
virtual QSize minimumOuterSizeHint() const Q_DECL_OVERRIDE
void started(QMouseEvent *event)
QString unicodeFraction(int numerator, int denominator) const
Groups multiple QCPBars together so they appear side by side.
Definition: qcustomplot.h:5383
0x010 Axes are selectable (or parts of them, see QCPAxis::setSelectableParts)
Definition: qcustomplot.h:263
void initializeParentPlot(QCustomPlot *parentPlot)
double lower
Definition: qcustomplot.h:783
QCPScatterStyle getFinalScatterStyle(const QCPScatterStyle &unselectedStyle) const
void setSelectedBrush(const QBrush &brush)
void setScatterSkip(int skip)
int graphCount() const
void setSelectedTickLabelColor(const QColor &color)
QSize minimumSize() const
Definition: qcustomplot.h:1234
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const
QFont font() const
Definition: qcustomplot.h:4733
void setTickStep(double step)
bool removePlottable(QCPAbstractPlottable *plottable)
Qt::Orientations rangeDrag() const
Definition: qcustomplot.h:4609
void setTail(const QCPLineEnding &tail)
bool isEmpty() const
QList< QCPAxis * > rangeDragAxes(Qt::Orientation orientation)
void setTickLabelColor(const QColor &color)
A template base class for plottables with one-dimensional data.
Definition: qcustomplot.h:3876
void setSize(double size)
void setBrush(const QBrush &brush)
ScaleType scaleType() const
Definition: qcustomplot.h:2016
ScatterShape mShape
Definition: qcustomplot.h:2383
Resolution is given in dots per meter (dpm)
Definition: qcustomplot.h:173
Full hue cycle, with highest and lowest color red (suitable for periodic data, such as angles and pha...
Definition: qcustomplot.h:4467
void pixelsToCoords(double x, double y, double &key, double &value) const
void setSelectedPen(const QPen &pen)
QPen mainPen() const
QCPItemPosition *const topLeft
Definition: qcustomplot.h:6416
bool removeItem(int index)
SelectableParts selectableParts() const
Definition: qcustomplot.h:4848
0xFF all margins
Definition: qcustomplot.h:208
QPixmap pixmap() const
Definition: qcustomplot.h:6466
QFont mainFont() const
bool mTightBoundary
Definition: qcustomplot.h:5799
friend class QCPPlottableLegendItem
Definition: qcustomplot.h:3395
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
bool mRangeReversed
Definition: qcustomplot.h:2167
void setupTickVectors()
virtual ~QCPLayerable()
PositionType mPositionTypeX
Definition: qcustomplot.h:3497
Qt::Alignment mPositionAlignment
Definition: qcustomplot.h:6365
QList< QCPAxisRect * > axisRects() const
double symbolGap() const
Definition: qcustomplot.h:6022
Holds multiple axes and arranges them in a rectangular shape.
Definition: qcustomplot.h:4590
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
QCPDataRange bounded(const QCPDataRange &other) const
virtual ~QCPAxisTicker()
QCPAxis * yAxis
Definition: qcustomplot.h:3739
void setPen(const QPen &pen)
QBrush brush() const
Definition: qcustomplot.h:6404
Whether to use immediate or queued refresh depends on whether the plotting hint QCP::phImmediateRefre...
Definition: qcustomplot.h:3623
int width() const
Definition: qcustomplot.h:4656
void clicked(QMouseEvent *event)
virtual ~QCPLayoutInset()
virtual QSize minimumSizeHint() const Q_DECL_OVERRIDE
virtual void parentPlotInitialized(QCustomPlot *parentPlot) Q_DECL_OVERRIDE
{ssDiamond.png} a diamond
Definition: qcustomplot.h:2333
QCPAxisRect * clipAxisRect() const
static QSize getFinalMinimumOuterSize(const QCPLayoutElement *el)
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
void rescaleValueAxis(bool onlyEnlarge=false, bool inKeyRange=false) const
virtual double dataMainKey(int index) const =0
QCP::AntialiasedElements mAADragBackup
Definition: qcustomplot.h:4684
QCPColorMapData & operator=(const QCPColorMapData &other)
bool setParentAnchorY(QCPItemAnchor *parentAnchor, bool keepPixelPosition=false)
void toPainter(QCPPainter *painter, int width=0, int height=0)
virtual void wheelEvent(QWheelEvent *event)
QPixmap toPixmap(int width=0, int height=0, double scale=1.0)
QCPRange mRange
Definition: qcustomplot.h:2166
QCPScatterStyle::ScatterProperties mUsedScatterProperties
Definition: qcustomplot.h:3281
double mBaseValue
Definition: qcustomplot.h:5544
QPen getTickPen() const
QBrush brush() const
Definition: qcustomplot.h:6248
Qt::KeyboardModifier mMultiSelectModifier
Definition: qcustomplot.h:3783
QCPLegend * legend
Definition: qcustomplot.h:3740
QCPRange range(const QCPAxis *axis) const
virtual void drawScatterPlot(QCPPainter *painter, const QVector< QPointF > &scatters, const QCPScatterStyle &style) const
void setSizeConstraintRect(SizeConstraintRect constraintRect)
void setPenNegative(const QPen &pen)
virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE
void setLabel(const QString &str)
QVector< QPointF > dataToImpulseLines(const QVector< QCPGraphData > &data) const
QCP::AntialiasedElements notAntialiasedElements() const
Definition: qcustomplot.h:3639
QCPItemAnchor *const top
Definition: qcustomplot.h:6262
void setAntialiasedElements(const QCP::AntialiasedElements &antialiasedElements)
virtual void applyDefaultAntialiasingHint(QCPPainter *painter) const Q_DECL_OVERRIDE
AxisType mAxisType
Definition: qcustomplot.h:2136
QCPSelectionRect(QCustomPlot *parentPlot)
Q_SLOT void updateLegendIcon(Qt::TransformationMode transformMode=Qt::SmoothTransformation, const QSize &thumbSize=QSize(32, 18))
virtual QPointF anchorPixelPosition(int anchorId) const Q_DECL_OVERRIDE
Milliseconds, one thousandth of a second (%z in setTimeFormat)
Definition: qcustomplot.h:1652
Q_SLOT void axisSelectionChanged(QCPAxis::SelectableParts selectedParts)
bool mAntialiasedSubGrid
Definition: qcustomplot.h:1901
void sizeConstraintsChanged() const
Q_SLOT void setRange(const QCPRange &range)
QVector< double > outliers
Definition: qcustomplot.h:5593
LineStyle mLineStyle
Definition: qcustomplot.h:5217
QList< QCPAxis * > selectedAxes() const
A filled arrow head with an indented back.
Definition: qcustomplot.h:1494
line is drawn as steps where the step height is the value of the right data point ...
Definition: qcustomplot.h:5180
virtual void deselectEvent(bool *selectionStateChanged)
QCP::Interactions mInteractions
Definition: qcustomplot.h:3773
QCPGraph * mGraph
Definition: qcustomplot.h:6586
QCPItemAnchor *const topRight
Definition: qcustomplot.h:6263
MarginSide
Definition: qcustomplot.h:204
Qt::Alignment mTextAlignment
Definition: qcustomplot.h:6366
void setLabel(const QString &str)
QPen mPen
Definition: qcustomplot.h:1902
QLatin1Char mNumberFormatChar
Definition: qcustomplot.h:2156
bool mBackgroundScaled
Definition: qcustomplot.h:4674
void setData(QSharedPointer< QCPCurveDataContainer > data)
The errors are for the value dimension (bars appear parallel to the value axis)
Definition: qcustomplot.h:6011
int axisCount(QCPAxis::AxisType type) const
QCP::AntialiasedElements mOpenGlAntialiasedElementsBackup
Definition: qcustomplot.h:3799
QBrush brush() const
Definition: qcustomplot.h:3319
AntialiasedElement
Definition: qcustomplot.h:222
QCPItemPosition *const left
Definition: qcustomplot.h:6646
bool mColorBufferInvalidated
Definition: qcustomplot.h:4506
int axisRectCount() const
QCustomPlot * mParentPlot
Definition: qcustomplot.h:2276
void setSubTicks(bool show)
QSharedPointer< QCPAxisTicker > mTicker
Definition: qcustomplot.h:2173
QPointer< QCPGraph > mChannelFillGraph
Definition: qcustomplot.h:5220
void setInvalidated(bool invalidated=true)
QVector< QPointF > dataToLines(const QVector< QCPGraphData > &data) const
virtual QCPRange getValueRange(bool &foundRange, QCP::SignDomain inSignDomain=QCP::sdBoth, const QCPRange &inKeyRange=QCPRange()) const Q_DECL_OVERRIDE
AxisType axisType() const
Definition: qcustomplot.h:2014
void setChannelFillGraph(QCPGraph *targetGraph)
void selectableChanged(QCP::SelectionType selectable)
void setWidth(double width)
QPointF toPointF() const
Definition: qcustomplot.h:412
double keyPixelOffset(const QCPBars *bars, double keyCoord)
double mWidth
Definition: qcustomplot.h:5541
No ending decoration.
Definition: qcustomplot.h:1492
bool hasElement(int row, int column)
QPen mSelectedPen
Definition: qcustomplot.h:6361
QCustomPlot * mParentPlot
Definition: qcustomplot.h:739
ScatterShape shape() const
Definition: qcustomplot.h:2357
void setSelectedBrush(const QBrush &brush)
void coordToCell(double key, double value, int *keyIndex, int *valueIndex) const
void drawShape(QCPPainter *painter, const QPointF &pos) const
void setPen(const QPen &pen)
QCPItemPosition *const bottomRight
Definition: qcustomplot.h:6261
Qt::AspectRatioMode mBackgroundScaledMode
Definition: qcustomplot.h:3780
QBrush mainBrush() const
virtual void drawCurveLine(QCPPainter *painter, const QVector< QPointF > &lines) const
Q_SLOT void setSelected(bool selected)
QCPLayerable(QCustomPlot *plot, QString targetLayer=QString(), QCPLayerable *parentLayerable=0)
double pointDistance(const QPointF &pixelPoint, QCPErrorBarsDataContainer::const_iterator &closestData) const
virtual void mouseMoveEvent(QMouseEvent *event, const QPointF &startPos) Q_DECL_OVERRIDE
virtual double selectTest(const QPointF &pos, bool onlySelectable, QVariant *details=0) const Q_DECL_OVERRIDE
bool selected() const
Definition: qcustomplot.h:4738
virtual ~QCPFinancial()
virtual void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE
int right() const
Definition: qcustomplot.h:4653
void afterReplot()
QCPFinancial(QCPAxis *keyAxis, QCPAxis *valueAxis)
QColor mSelectedLabelColor
Definition: qcustomplot.h:2148
void setFont(const QFont &font)
void setColorStopAt(double position, const QColor &color)
bool mTicks
Definition: qcustomplot.h:2160
virtual double getTickStep(const QCPRange &range) Q_DECL_OVERRIDE
QCPVector2D & operator*=(double factor)
void setLabelPadding(int padding)